summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--apex/Android.bp1
-rw-r--r--apex/appsearch/framework/api/current.txt71
-rw-r--r--apex/appsearch/framework/java/TEST_MAPPING (renamed from apex/appsearch/framework/java/android/app/TEST_MAPPING)0
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java5
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java6
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java8
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java6
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl3
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java103
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java (renamed from apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java)0
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java (renamed from apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java)56
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java (renamed from apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java)146
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java (renamed from apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java)1
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java (renamed from apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java)1
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java (renamed from apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java)1
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java (renamed from apex/appsearch/framework/java/android/app/appsearch/SearchResult.java)0
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java (renamed from apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java)0
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java (renamed from apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java)0
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java179
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java (renamed from apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java)0
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java (renamed from apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java)0
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java (renamed from apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java)0
-rw-r--r--apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java221
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java8
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java96
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java111
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java8
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java78
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java10
-rw-r--r--apex/appsearch/synced_jetpack_changeid.txt2
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java77
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobParameters.java19
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java27
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java32
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java54
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java1066
-rw-r--r--apex/statsd/framework/Android.bp6
-rw-r--r--cmds/statsd/.clang-format17
-rw-r--r--cmds/statsd/Android.bp444
-rw-r--r--cmds/statsd/OWNERS1
-rw-r--r--cmds/statsd/TEST_MAPPING17
-rw-r--r--cmds/statsd/benchmark/duration_metric_benchmark.cpp314
-rw-r--r--cmds/statsd/benchmark/filter_value_benchmark.cpp72
-rw-r--r--cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp77
-rw-r--r--cmds/statsd/benchmark/hello_world_benchmark.cpp29
-rw-r--r--cmds/statsd/benchmark/log_event_benchmark.cpp50
-rw-r--r--cmds/statsd/benchmark/metric_util.cpp379
-rw-r--r--cmds/statsd/benchmark/metric_util.h140
-rw-r--r--cmds/statsd/benchmark/stats_write_benchmark.cpp40
-rw-r--r--cmds/statsd/src/FieldValue.cpp474
-rw-r--r--cmds/statsd/src/FieldValue.h462
-rw-r--r--cmds/statsd/src/HashableDimensionKey.cpp381
-rw-r--r--cmds/statsd/src/HashableDimensionKey.h238
-rw-r--r--cmds/statsd/src/Log.h33
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp1145
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h374
-rw-r--r--cmds/statsd/src/StatsService.cpp1312
-rw-r--r--cmds/statsd/src/StatsService.h416
-rw-r--r--cmds/statsd/src/active_config_list.proto57
-rw-r--r--cmds/statsd/src/annotations.h33
-rw-r--r--cmds/statsd/src/anomaly/AlarmMonitor.cpp139
-rw-r--r--cmds/statsd/src/anomaly/AlarmMonitor.h162
-rw-r--r--cmds/statsd/src/anomaly/AlarmTracker.cpp94
-rw-r--r--cmds/statsd/src/anomaly/AlarmTracker.h81
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp326
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.h229
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp115
-rw-r--r--cmds/statsd/src/anomaly/DurationAnomalyTracker.h79
-rw-r--r--cmds/statsd/src/anomaly/indexed_priority_queue.h224
-rw-r--r--cmds/statsd/src/anomaly/subscriber_util.cpp70
-rw-r--r--cmds/statsd/src/anomaly/subscriber_util.h33
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.cpp244
-rw-r--r--cmds/statsd/src/condition/CombinationConditionTracker.h117
-rw-r--r--cmds/statsd/src/condition/ConditionTimer.h107
-rw-r--r--cmds/statsd/src/condition/ConditionTracker.h187
-rw-r--r--cmds/statsd/src/condition/ConditionWizard.cpp70
-rw-r--r--cmds/statsd/src/condition/ConditionWizard.h68
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.cpp409
-rw-r--r--cmds/statsd/src/condition/SimpleConditionTracker.h145
-rw-r--r--cmds/statsd/src/condition/condition_util.cpp93
-rw-r--r--cmds/statsd/src/condition/condition_util.h43
-rw-r--r--cmds/statsd/src/config/ConfigKey.cpp54
-rw-r--r--cmds/statsd/src/config/ConfigKey.h89
-rw-r--r--cmds/statsd/src/config/ConfigListener.cpp31
-rw-r--r--cmds/statsd/src/config/ConfigListener.h52
-rw-r--r--cmds/statsd/src/config/ConfigManager.cpp375
-rw-r--r--cmds/statsd/src/config/ConfigManager.h181
-rw-r--r--cmds/statsd/src/external/Perfetto.cpp139
-rw-r--r--cmds/statsd/src/external/Perfetto.h37
-rw-r--r--cmds/statsd/src/external/PullDataReceiver.h41
-rw-r--r--cmds/statsd/src/external/PullResultReceiver.cpp39
-rw-r--r--cmds/statsd/src/external/PullResultReceiver.h48
-rw-r--r--cmds/statsd/src/external/PullUidProvider.h39
-rw-r--r--cmds/statsd/src/external/StatsCallbackPuller.cpp116
-rw-r--r--cmds/statsd/src/external/StatsCallbackPuller.h46
-rw-r--r--cmds/statsd/src/external/StatsPuller.cpp126
-rw-r--r--cmds/statsd/src/external/StatsPuller.h119
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp371
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.h189
-rw-r--r--cmds/statsd/src/external/TrainInfoPuller.cpp55
-rw-r--r--cmds/statsd/src/external/TrainInfoPuller.h38
-rw-r--r--cmds/statsd/src/external/puller_util.cpp153
-rw-r--r--cmds/statsd/src/external/puller_util.h34
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp1101
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h678
-rw-r--r--cmds/statsd/src/hash.cpp142
-rw-r--r--cmds/statsd/src/hash.h47
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp599
-rw-r--r--cmds/statsd/src/logd/LogEvent.h331
-rw-r--r--cmds/statsd/src/logd/LogEventQueue.cpp62
-rw-r--r--cmds/statsd/src/logd/LogEventQueue.h57
-rw-r--r--cmds/statsd/src/main.cpp113
-rw-r--r--cmds/statsd/src/matchers/AtomMatchingTracker.h118
-rw-r--r--cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp140
-rw-r--r--cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h58
-rw-r--r--cmds/statsd/src/matchers/EventMatcherWizard.cpp35
-rw-r--r--cmds/statsd/src/matchers/EventMatcherWizard.h41
-rw-r--r--cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp81
-rw-r--r--cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h58
-rw-r--r--cmds/statsd/src/matchers/matcher_util.cpp373
-rw-r--r--cmds/statsd/src/matchers/matcher_util.h44
-rw-r--r--cmds/statsd/src/metadata_util.cpp122
-rw-r--r--cmds/statsd/src/metadata_util.h32
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp440
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h143
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp747
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h194
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp214
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h103
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp675
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h234
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp355
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h582
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp773
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h383
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp1282
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h396
-rw-r--r--cmds/statsd/src/metrics/duration_helper/DurationTracker.h240
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp331
-rw-r--r--cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h92
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp462
-rw-r--r--cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h93
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp1118
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/config_update_utils.h276
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp1220
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h342
-rw-r--r--cmds/statsd/src/packages/PackageInfoListener.h47
-rw-r--r--cmds/statsd/src/packages/UidMap.cpp563
-rw-r--r--cmds/statsd/src/packages/UidMap.h226
-rw-r--r--cmds/statsd/src/shell/ShellSubscriber.cpp245
-rw-r--r--cmds/statsd/src/shell/ShellSubscriber.h146
-rw-r--r--cmds/statsd/src/shell/shell_config.proto39
-rw-r--r--cmds/statsd/src/shell/shell_data.proto29
-rwxr-xr-xcmds/statsd/src/socket/StatsSocketListener.cpp156
-rw-r--r--cmds/statsd/src/socket/StatsSocketListener.h54
-rw-r--r--cmds/statsd/src/state/StateListener.h54
-rw-r--r--cmds/statsd/src/state/StateManager.cpp112
-rw-r--r--cmds/statsd/src/state/StateManager.h103
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp190
-rw-r--r--cmds/statsd/src/state/StateTracker.h94
-rw-r--r--cmds/statsd/src/stats_log.proto553
-rw-r--r--cmds/statsd/src/stats_log_util.cpp609
-rw-r--r--cmds/statsd/src/stats_log_util.h117
-rw-r--r--cmds/statsd/src/stats_util.h36
-rw-r--r--cmds/statsd/src/statscompanion_util.cpp35
-rw-r--r--cmds/statsd/src/statscompanion_util.h33
-rw-r--r--cmds/statsd/src/statsd_config.proto511
-rw-r--r--cmds/statsd/src/statsd_metadata.proto67
-rw-r--r--cmds/statsd/src/storage/StorageManager.cpp781
-rw-r--r--cmds/statsd/src/storage/StorageManager.h168
-rw-r--r--cmds/statsd/src/subscriber/IncidentdReporter.cpp169
-rw-r--r--cmds/statsd/src/subscriber/IncidentdReporter.h36
-rw-r--r--cmds/statsd/src/subscriber/SubscriberReporter.cpp171
-rw-r--r--cmds/statsd/src/subscriber/SubscriberReporter.h109
-rw-r--r--cmds/statsd/src/uid_data.proto36
-rw-r--r--cmds/statsd/src/utils/MultiConditionTrigger.cpp57
-rw-r--r--cmds/statsd/src/utils/MultiConditionTrigger.h55
-rw-r--r--cmds/statsd/statsd_test.xml37
-rw-r--r--cmds/statsd/tests/AlarmMonitor_test.cpp69
-rw-r--r--cmds/statsd/tests/ConfigManager_test.cpp159
-rw-r--r--cmds/statsd/tests/FieldValue_test.cpp659
-rw-r--r--cmds/statsd/tests/HashableDimensionKey_test.cpp137
-rw-r--r--cmds/statsd/tests/LogEntryMatcher_test.cpp809
-rw-r--r--cmds/statsd/tests/LogEvent_test.cpp371
-rw-r--r--cmds/statsd/tests/LogReader_test.cpp21
-rw-r--r--cmds/statsd/tests/MetricsManager_test.cpp325
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp1886
-rw-r--r--cmds/statsd/tests/StatsService_test.cpp104
-rw-r--r--cmds/statsd/tests/UidMap_test.cpp426
-rw-r--r--cmds/statsd/tests/anomaly/AlarmTracker_test.cpp94
-rw-r--r--cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp408
-rw-r--r--cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp167
-rw-r--r--cmds/statsd/tests/condition/ConditionTimer_test.cpp68
-rw-r--r--cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp741
-rw-r--r--cmds/statsd/tests/e2e/Alarm_e2e_test.cpp92
-rw-r--r--cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp390
-rw-r--r--cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp527
-rw-r--r--cmds/statsd/tests/e2e/Attribution_e2e_test.cpp375
-rw-r--r--cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp114
-rw-r--r--cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp307
-rw-r--r--cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp901
-rw-r--r--cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp1473
-rw-r--r--cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp624
-rw-r--r--cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp295
-rw-r--r--cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp1833
-rw-r--r--cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp348
-rw-r--r--cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp433
-rw-r--r--cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp679
-rw-r--r--cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp355
-rw-r--r--cmds/statsd/tests/external/StatsCallbackPuller_test.cpp219
-rw-r--r--cmds/statsd/tests/external/StatsPullerManager_test.cpp150
-rw-r--r--cmds/statsd/tests/external/StatsPuller_test.cpp316
-rw-r--r--cmds/statsd/tests/external/puller_util_test.cpp408
-rw-r--r--cmds/statsd/tests/guardrail/StatsdStats_test.cpp544
-rw-r--r--cmds/statsd/tests/indexed_priority_queue_test.cpp235
-rw-r--r--cmds/statsd/tests/log_event/LogEventQueue_test.cpp115
-rw-r--r--cmds/statsd/tests/metadata_util_test.cpp69
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp477
-rw-r--r--cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp516
-rw-r--r--cmds/statsd/tests/metrics/EventMetricProducer_test.cpp186
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp828
-rw-r--r--cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp424
-rw-r--r--cmds/statsd/tests/metrics/OringDurationTracker_test.cpp576
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp6919
-rw-r--r--cmds/statsd/tests/metrics/metrics_test_helper.cpp57
-rw-r--r--cmds/statsd/tests/metrics/metrics_test_helper.h63
-rw-r--r--cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp3632
-rw-r--r--cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp890
-rw-r--r--cmds/statsd/tests/shell/ShellSubscriber_test.cpp207
-rw-r--r--cmds/statsd/tests/state/StateTracker_test.cpp571
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp1409
-rw-r--r--cmds/statsd/tests/statsd_test_util.h483
-rw-r--r--cmds/statsd/tests/storage/StorageManager_test.cpp204
-rw-r--r--cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp174
-rw-r--r--cmds/statsd/tools/localtools/Android.bp46
-rw-r--r--cmds/statsd/tools/localtools/TEST_MAPPING8
-rw-r--r--cmds/statsd/tools/localtools/localdrive_manifest.txt1
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java284
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java379
-rw-r--r--cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java419
-rw-r--r--cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java326
-rw-r--r--cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java195
-rw-r--r--cmds/statsd/tools/localtools/testdrive_manifest.txt1
-rw-r--r--core/api/current.txt253
-rw-r--r--core/api/system-current.txt172
-rw-r--r--core/api/test-current.txt10
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java22
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl2
-rw-r--r--core/java/android/app/Activity.java328
-rw-r--r--core/java/android/app/ActivityClient.java476
-rw-r--r--core/java/android/app/ActivityManager.java39
-rw-r--r--core/java/android/app/ActivityThread.java109
-rw-r--r--core/java/android/app/ApplicationPackageManager.java15
-rw-r--r--core/java/android/app/IActivityClientController.aidl108
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl88
-rw-r--r--core/java/android/app/LoadedApk.java2
-rw-r--r--core/java/android/app/Notification.java4
-rw-r--r--core/java/android/app/NotificationChannel.java81
-rw-r--r--core/java/android/app/NotificationChannelGroup.java20
-rw-r--r--core/java/android/app/ResourcesManager.java2
-rw-r--r--core/java/android/app/SystemServiceRegistry.java9
-rw-r--r--core/java/android/app/TEST_MAPPING12
-rw-r--r--core/java/android/app/admin/DeviceAdminInfo.java12
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java157
-rw-r--r--core/java/android/app/admin/FactoryResetProtectionPolicy.java16
-rw-r--r--core/java/android/app/admin/SystemUpdateInfo.java30
-rw-r--r--core/java/android/app/admin/SystemUpdatePolicy.java59
-rw-r--r--core/java/android/app/backup/BackupManager.java9
-rw-r--r--core/java/android/app/role/IRoleManager.aidl6
-rw-r--r--core/java/android/app/role/RoleManager.java56
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java11
-rw-r--r--core/java/android/app/servertransaction/PendingTransactionActions.java12
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java11
-rw-r--r--core/java/android/app/servertransaction/TopResumedActivityChangeItem.java9
-rw-r--r--core/java/android/app/usage/UsageEvents.java4
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java3
-rwxr-xr-xcore/java/android/bluetooth/BluetoothClass.java4
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/content/ContextWrapper.java143
-rw-r--r--core/java/android/content/SyncAdaptersCache.java8
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl1
-rw-r--r--core/java/android/content/pm/IntentFilterVerificationInfo.java29
-rw-r--r--core/java/android/content/pm/PackageInstaller.java25
-rw-r--r--core/java/android/content/pm/PackageManager.java22
-rw-r--r--core/java/android/content/pm/PackageParser.java30
-rw-r--r--core/java/android/content/pm/PackageUserState.java6
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java20
-rw-r--r--core/java/android/content/pm/SuspendDialogInfo.java35
-rw-r--r--core/java/android/content/pm/XmlSerializerAndParser.java16
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java69
-rw-r--r--core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl2
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java4
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java5
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java5
-rw-r--r--core/java/android/hardware/display/BrightnessConfiguration.java59
-rw-r--r--core/java/android/hardware/display/BrightnessCorrection.java27
-rw-r--r--core/java/android/hardware/display/DisplayManager.java61
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java27
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl6
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl4
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl4
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java42
-rw-r--r--core/java/android/hardware/usb/UsbManager.java2
-rw-r--r--core/java/android/hardware/usb/UsbPortStatus.java4
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java19
-rw-r--r--core/java/android/net/ConnectivityMetricsEvent.java6
-rw-r--r--core/java/android/net/Ikev2VpnProfile.java15
-rw-r--r--core/java/android/net/IpSecTransform.java104
-rw-r--r--core/java/android/net/LinkProperties.java3
-rw-r--r--core/java/android/net/NetworkIdentity.java18
-rw-r--r--core/java/android/net/NetworkPolicyManager.java2
-rw-r--r--core/java/android/net/NetworkProvider.java7
-rw-r--r--core/java/android/net/metrics/ConnectStats.java11
-rw-r--r--core/java/android/net/metrics/DefaultNetworkEvent.java15
-rw-r--r--core/java/android/net/metrics/DnsEvent.java13
-rw-r--r--core/java/android/net/metrics/NetworkMetrics.java8
-rw-r--r--core/java/android/os/BatteryConsumer.java89
-rw-r--r--core/java/android/os/BatteryStatsManager.java16
-rw-r--r--core/java/android/os/BatteryUsageStats.aidl (renamed from cmds/statsd/benchmark/main.cpp)6
-rw-r--r--core/java/android/os/BatteryUsageStats.java138
-rw-r--r--core/java/android/os/Debug.java8
-rw-r--r--core/java/android/os/FileUtils.java3
-rw-r--r--core/java/android/os/PowerComponents.java170
-rw-r--r--core/java/android/os/UidBatteryConsumer.java145
-rw-r--r--core/java/android/permission/IPermissionManager.aidl8
-rw-r--r--core/java/android/permission/PermissionManagerInternal.java100
-rw-r--r--core/java/android/provider/ContactsContract.java323
-rw-r--r--core/java/android/provider/DeviceConfig.java8
-rw-r--r--core/java/android/provider/Settings.java33
-rw-r--r--core/java/android/service/carrier/CarrierMessagingService.java39
-rw-r--r--core/java/android/service/carrier/CarrierMessagingServiceWrapper.java196
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java15
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java96
-rw-r--r--core/java/android/telephony/PhoneStateListener.java1415
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java354
-rwxr-xr-xcore/java/android/text/format/DateFormat.java2
-rw-r--r--core/java/android/util/Xml.java45
-rw-r--r--core/java/android/uwb/AdapterStateListener.java149
-rw-r--r--core/java/android/uwb/IUwbAdapter.aidl11
-rw-r--r--core/java/android/uwb/StateChangeReason.aidl5
-rw-r--r--core/java/android/uwb/UwbManager.java123
-rw-r--r--core/java/android/view/ContentInfo.java59
-rw-r--r--core/java/android/view/Display.java3
-rw-r--r--core/java/android/view/IPinnedStackController.aidl32
-rw-r--r--core/java/android/view/IPinnedStackListener.aidl7
-rw-r--r--core/java/android/view/IWindowManager.aidl21
-rw-r--r--core/java/android/view/InsetsController.java19
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java12
-rw-r--r--core/java/android/view/OnReceiveContentListener.java12
-rw-r--r--core/java/android/view/ThreadedRenderer.java8
-rw-r--r--core/java/android/view/View.java26
-rw-r--r--core/java/android/view/ViewFrameInfo.java81
-rw-r--r--core/java/android/view/ViewRootImpl.java42
-rw-r--r--core/java/android/view/Window.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java89
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl4
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManagerClient.aidl2
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java16
-rw-r--r--core/java/android/view/textclassifier/TextSelection.java71
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java5
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java3
-rw-r--r--core/java/android/window/TaskOrganizer.java4
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java34
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java13
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java9
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl4
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java47
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java76
-rw-r--r--core/java/com/android/internal/os/KernelWakelockReader.java1
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl4
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl15
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java7
-rw-r--r--core/java/com/android/internal/util/BinaryXmlSerializer.java2
-rw-r--r--core/java/com/android/internal/util/XmlUtils.java15
-rw-r--r--core/java/com/android/internal/widget/DecorCaptionView.java9
-rw-r--r--core/java/com/android/server/BootReceiver.java16
-rw-r--r--core/java/com/android/server/backup/PermissionBackupHelper.java10
-rw-r--r--core/jni/android_os_Debug.cpp3
-rw-r--r--core/proto/android/server/jobscheduler.proto56
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/drawable/view_accessibility_focused.xml4
-rw-r--r--core/res/res/layout/notification_top_line_views.xml2
-rw-r--r--core/res/res/values/colors.xml2
-rw-r--r--core/res/res/values/config.xml30
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/strings.xml9
-rw-r--r--core/res/res/values/symbols.xml9
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java65
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java121
-rw-r--r--core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java254
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java8
-rw-r--r--core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java10
-rw-r--r--core/tests/coretests/src/android/util/BinaryXmlTest.java61
-rw-r--r--core/tests/coretests/src/android/util/XmlTest.java4
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java25
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java4
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java16
-rw-r--r--core/tests/overlaytests/remount/TEST_MAPPING4
-rw-r--r--core/tests/uwbtests/Android.bp1
-rw-r--r--core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java311
-rw-r--r--core/tests/uwbtests/src/android/uwb/UwbManagerTest.java49
-rw-r--r--core/tests/uwbtests/src/android/uwb/UwbTestUtils.java7
-rw-r--r--data/etc/privapp-permissions-platform.xml5
-rw-r--r--data/etc/services.core.protolog.json72
-rw-r--r--errorprone/refaster/EfficientXml.java24
-rw-r--r--errorprone/refaster/EfficientXml.java.refasterbin36469 -> 38713 bytes
-rw-r--r--graphics/java/android/graphics/FrameInfo.java40
-rw-r--r--graphics/java/android/graphics/Typeface.java2
-rw-r--r--graphics/java/android/graphics/fonts/Font.java52
-rw-r--r--graphics/java/android/graphics/fonts/NativeFont.java205
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java36
-rw-r--r--libs/WindowManager/Shell/Android.bp4
-rw-r--r--libs/WindowManager/Shell/res/layout/split_divider.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java159
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java91
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java211
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java240
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java171
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java205
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java115
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java102
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java106
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java)68
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java155
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java)5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java123
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java236
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java102
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java)163
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java98
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt114
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java89
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java106
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java66
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java10
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/FrameInfo.cpp6
-rw-r--r--libs/hwui/FrameInfo.h3
-rw-r--r--libs/hwui/Layer.cpp86
-rw-r--r--libs/hwui/Layer.h3
-rw-r--r--libs/hwui/apex/jni_runtime.cpp2
-rw-r--r--libs/hwui/canvas/CanvasOpRasterizer.cpp6
-rw-r--r--libs/hwui/canvas/CanvasOpTypes.h3
-rw-r--r--libs/hwui/canvas/CanvasOps.h39
-rw-r--r--libs/hwui/canvas/Points.h48
-rw-r--r--libs/hwui/jni/fonts/Font.cpp35
-rw-r--r--libs/hwui/jni/fonts/NativeFont.cpp125
-rw-r--r--libs/hwui/tests/unit/CanvasOpTests.cpp49
-rw-r--r--location/java/android/location/LocationManager.java13
-rw-r--r--media/java/android/media/AudioSystem.java3
-rw-r--r--media/java/android/media/MediaPlayer.java6
-rw-r--r--media/java/android/media/session/MediaController.java15
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java45
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java109
-rw-r--r--media/jni/android_media_MediaCodecLinearBlock.h9
-rw-r--r--native/android/Android.bp5
-rw-r--r--native/android/activity_manager.cpp226
-rw-r--r--native/android/include_platform/android/activity_manager.h176
-rw-r--r--native/android/libandroid.map.txt4
-rw-r--r--native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp11
-rw-r--r--native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml33
-rw-r--r--native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java107
-rw-r--r--native/android/tests/activitymanager/nativeTests/Android.bp39
-rw-r--r--native/android/tests/activitymanager/nativeTests/AndroidTest.xml53
-rw-r--r--native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp139
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java5
-rw-r--r--packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java29
-rw-r--r--packages/SettingsLib/res/values/strings.xml12
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java10
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java4
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java5
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java32
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java16
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java11
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java4
-rw-r--r--packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java36
-rw-r--r--packages/Shell/AndroidManifest.xml12
-rw-r--r--packages/SystemUI/AndroidManifest.xml14
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java7
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml8
-rw-r--r--packages/SystemUI/res-keyguard/values/attrs.xml5
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml4
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml1
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java31
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java47
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java147
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java246
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java407
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java113
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java171
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java9
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java39
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java32
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java53
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java5
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java162
-rw-r--r--services/art-profile54
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java4
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java209
-rw-r--r--services/backup/Android.bp2
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java4
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java9
-rw-r--r--services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java65
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java75
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java136
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java6
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java102
-rw-r--r--services/core/java/com/android/server/SensorPrivacyService.java2
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java839
-rw-r--r--services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java8
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java81
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java4
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java14
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java1
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java40
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java17
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java25
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java31
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java10
-rw-r--r--services/core/java/com/android/server/connectivity/DnsManager.java22
-rw-r--r--services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java37
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java42
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkNotificationManager.java14
-rw-r--r--services/core/java/com/android/server/connectivity/ProxyTracker.java10
-rw-r--r--services/core/java/com/android/server/content/ContentService.java7
-rw-r--r--services/core/java/com/android/server/content/SyncStorageEngine.java24
-rw-r--r--services/core/java/com/android/server/display/BrightnessTracker.java7
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java27
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java37
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java47
-rw-r--r--services/core/java/com/android/server/display/PersistentDataStore.java16
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecConfig.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java10
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecNetwork.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java11
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java6
-rw-r--r--services/core/java/com/android/server/input/PersistentDataStore.java2
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java6
-rw-r--r--services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java2
-rw-r--r--services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java44
-rw-r--r--services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java12
-rw-r--r--services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java26
-rw-r--r--services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java5
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java20
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyLogger.java4
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java79
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java110
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java2
-rw-r--r--services/core/java/com/android/server/notification/ShortcutHelper.java5
-rw-r--r--services/core/java/com/android/server/notification/SnoozeHelper.java4
-rw-r--r--services/core/java/com/android/server/notification/VisibilityExtractor.java49
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java3
-rw-r--r--services/core/java/com/android/server/om/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/pm/DefaultAppProvider.java177
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java176
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java910
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java25
-rw-r--r--services/core/java/com/android/server/pm/Settings.java100
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java33
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java23
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermission.java10
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java172
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java181
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java604
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java557
-rw-r--r--services/core/java/com/android/server/policy/DeviceStateProviderImpl.java35
-rw-r--r--services/core/java/com/android/server/policy/KeyCombinationManager.java229
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java405
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java6
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java442
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java124
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java45
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java176
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java15
-rw-r--r--services/core/java/com/android/server/slice/SliceManagerService.java5
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java27
-rw-r--r--services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java77
-rw-r--r--services/core/java/com/android/server/telecom/TelecomLoaderService.java17
-rw-r--r--services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java2
-rw-r--r--services/core/java/com/android/server/tv/PersistentDataStore.java13
-rw-r--r--services/core/java/com/android/server/tv/TEST_MAPPING7
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java41
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java5
-rw-r--r--services/core/java/com/android/server/utils/quota/MultiRateLimiter.java183
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java1046
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java1074
-rw-r--r--services/core/java/com/android/server/wm/AppWarnings.java14
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainerListener.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java17
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicy.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java143
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java59
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java13
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java264
-rw-r--r--services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java25
-rw-r--r--services/core/java/com/android/server/wm/DragState.java3
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java26
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java2
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java16
-rw-r--r--services/core/java/com/android/server/wm/RootDisplayArea.java32
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java23
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java35
-rw-r--r--services/core/java/com/android/server/wm/WindowContainerListener.java (renamed from cmds/statsd/src/experiment_ids.proto)19
-rw-r--r--services/core/java/com/android/server/wm/WindowContextListenerController.java248
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java116
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java1
-rw-r--r--services/core/jni/Android.bp2
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp20
-rw-r--r--services/core/xsd/device-state-config/device-state-config.xsd2
-rw-r--r--services/core/xsd/device-state-config/schema/current.txt4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java173
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java95
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java14
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java53
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java7
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java2
-rw-r--r--services/tests/mockingservicestests/Android.bp20
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java1722
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt662
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt173
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java197
-rw-r--r--services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt41
-rw-r--r--services/tests/servicestests/Android.bp11
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java144
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java208
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java99
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java174
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java44
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java217
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/TvInputServiceManagerTest.java101
-rw-r--r--services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java5
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java9
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java69
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java48
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java4
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java27
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java246
-rw-r--r--services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java208
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java177
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java79
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java390
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java101
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java173
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java75
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java6
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java9
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java178
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java30
-rw-r--r--telecomm/TEST_MAPPING4
-rw-r--r--telecomm/java/android/telecom/BluetoothCallQualityReport.java256
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java61
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java13
-rw-r--r--telephony/java/android/telephony/CellInfo.java52
-rw-r--r--telephony/java/android/telephony/CellInfoCdma.java9
-rw-r--r--telephony/java/android/telephony/CellInfoGsm.java8
-rw-r--r--telephony/java/android/telephony/CellInfoLte.java9
-rw-r--r--telephony/java/android/telephony/CellInfoNr.java8
-rw-r--r--telephony/java/android/telephony/CellInfoTdscdma.java8
-rw-r--r--telephony/java/android/telephony/CellInfoWcdma.java8
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java63
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java106
-rw-r--r--telephony/java/android/telephony/PhoneCapability.java2
-rw-r--r--telephony/java/android/telephony/PhysicalChannelConfig.java12
-rw-r--r--telephony/java/android/telephony/SignalStrength.java15
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java165
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java15
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java2
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java74
-rw-r--r--tests/FlickerTests/AndroidManifest.xml5
-rw-r--r--tests/FlickerTests/AndroidTestPhysicalDevices.xml2
-rw-r--r--tests/FlickerTests/AndroidTestVirtualDevices.xml2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt2
-rw-r--r--tests/Input/Android.bp1
-rw-r--r--tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt78
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java77
-rw-r--r--tests/RollbackTest/TEST_MAPPING2
-rw-r--r--tests/StagedInstallTest/Android.bp1
-rw-r--r--tests/StagedInstallTest/TEST_MAPPING2
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java14
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt5
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt21
-rw-r--r--tests/net/common/java/android/net/NetworkProviderTest.kt14
-rw-r--r--tests/net/java/com/android/server/NetworkManagementServiceTest.java131
-rw-r--r--wifi/TEST_MAPPING5
-rw-r--r--wifi/api/current.txt2
-rw-r--r--wifi/api/system-current.txt2
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl4
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java6
-rw-r--r--wifi/java/android/net/wifi/WifiEnterpriseConfig.java20
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java100
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSuggestion.java3
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareManager.java4
-rw-r--r--wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java88
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java20
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java23
853 files changed, 27798 insertions, 78560 deletions
diff --git a/Android.bp b/Android.bp
index c47b74717dcd..949373317097 100644
--- a/Android.bp
+++ b/Android.bp
@@ -916,6 +916,7 @@ java_library_host {
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_internal_protos",
+ ":statsd_internal_protos",
"cmds/am/proto/instrumentation_data.proto",
"cmds/statsd/src/**/*.proto",
"core/proto/**/*.proto",
diff --git a/apex/Android.bp b/apex/Android.bp
index 0a535a8fe9b9..4e80acb64e05 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -15,6 +15,7 @@
mainline_stubs_args =
"--error UnhiddenSystemApi " +
"--hide BroadcastBehavior " +
+ "--hide CallbackInterface " +
"--hide DeprecationMismatch " +
"--hide HiddenSuperclass " +
"--hide HiddenTypedefConstant " +
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
index 2be873cc8bca..380c64679fab 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -1,6 +1,27 @@
// Signature format: 2.0
package android.app.appsearch {
+ public final class AppSearchBatchResult<KeyType, ValueType> {
+ method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getFailures();
+ method @NonNull public java.util.Map<KeyType,ValueType> getSuccesses();
+ method public boolean isSuccess();
+ }
+
+ public final class AppSearchResult<ValueType> {
+ method @Nullable public String getErrorMessage();
+ method public int getResultCode();
+ method @Nullable public ValueType getResultValue();
+ method public boolean isSuccess();
+ field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2
+ field public static final int RESULT_INVALID_ARGUMENT = 3; // 0x3
+ field public static final int RESULT_INVALID_SCHEMA = 7; // 0x7
+ field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_NOT_FOUND = 6; // 0x6
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_OUT_OF_SPACE = 5; // 0x5
+ field public static final int RESULT_UNKNOWN_ERROR = 1; // 0x1
+ }
+
public final class AppSearchSchema {
method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties();
method @NonNull public String getSchemaType();
@@ -85,6 +106,43 @@ package android.app.appsearch {
method @NonNull public BuilderType setTtlMillis(long);
}
+ public final class GetByUriRequest {
+ method @NonNull public String getNamespace();
+ method @NonNull public java.util.Set<java.lang.String> getUris();
+ }
+
+ public static final class GetByUriRequest.Builder {
+ ctor public GetByUriRequest.Builder();
+ method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.GetByUriRequest build();
+ method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String);
+ }
+
+ public final class PutDocumentsRequest {
+ method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getDocuments();
+ }
+
+ public static final class PutDocumentsRequest.Builder {
+ ctor public PutDocumentsRequest.Builder();
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull android.app.appsearch.GenericDocument...);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<android.app.appsearch.GenericDocument>);
+ method @NonNull public android.app.appsearch.PutDocumentsRequest build();
+ }
+
+ public final class RemoveByUriRequest {
+ method @NonNull public String getNamespace();
+ method @NonNull public java.util.Set<java.lang.String> getUris();
+ }
+
+ public static final class RemoveByUriRequest.Builder {
+ ctor public RemoveByUriRequest.Builder();
+ method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.lang.String...);
+ method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
+ method @NonNull public android.app.appsearch.RemoveByUriRequest build();
+ method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
+ }
+
public final class SearchResult {
method @NonNull public android.app.appsearch.GenericDocument getDocument();
method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
@@ -139,5 +197,18 @@ package android.app.appsearch {
method @NonNull public android.app.appsearch.SearchSpec.Builder setTermMatch(int);
}
+ public final class SetSchemaRequest {
+ method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
+ method public boolean isForceOverride();
+ }
+
+ public static final class SetSchemaRequest.Builder {
+ ctor public SetSchemaRequest.Builder();
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull android.app.appsearch.AppSearchSchema...);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
+ method @NonNull public android.app.appsearch.SetSchemaRequest build();
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
+ }
+
}
diff --git a/apex/appsearch/framework/java/android/app/TEST_MAPPING b/apex/appsearch/framework/java/TEST_MAPPING
index 12188f83a29f..12188f83a29f 100644
--- a/apex/appsearch/framework/java/android/app/TEST_MAPPING
+++ b/apex/appsearch/framework/java/TEST_MAPPING
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index 98daa66183a3..97cfe36fca80 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -33,7 +33,6 @@ import java.util.Map;
*
* @param <KeyType> The type of the keys for {@link #getSuccesses} and {@link #getFailures}.
* @param <ValueType> The type of result objects associated with the keys.
- * @hide
*/
public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
@NonNull private final Map<KeyType, ValueType> mSuccesses;
@@ -51,6 +50,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl
mFailures = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null));
}
+ /** @hide */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mSuccesses);
@@ -100,11 +100,14 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl
return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}";
}
+ /** @hide */
@Override
public int describeContents() {
return 0;
}
+ /** @hide */
+ @NonNull
public static final Creator<AppSearchBatchResult> CREATOR =
new Creator<AppSearchBatchResult>() {
@NonNull
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 5fd45eadbda9..442ca7b8639b 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -224,7 +224,11 @@ public class AppSearchManager {
}
AndroidFuture<AppSearchResult> future = new AndroidFuture<>();
try {
- mService.setSchema(DEFAULT_DATABASE_NAME, schemaBundles, request.isForceOverride(),
+ mService.setSchema(
+ DEFAULT_DATABASE_NAME,
+ schemaBundles,
+ new ArrayList<>(request.getSchemasNotPlatformSurfaceable()),
+ request.isForceOverride(),
new IAppSearchResultCallback.Stub() {
public void onResult(AppSearchResult result) {
future.complete(result);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
index 6e2ed70cca01..76225e40c56e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
@@ -32,7 +32,6 @@ import java.util.Objects;
* Information about the success or failure of an AppSearch call.
*
* @param <ValueType> The type of result object for successful calls.
- * @hide
*/
public final class AppSearchResult<ValueType> implements Parcelable {
/**
@@ -107,6 +106,7 @@ public final class AppSearchResult<ValueType> implements Parcelable {
mErrorMessage = in.readString();
}
+ /** @hide */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mResultCode);
@@ -181,13 +181,15 @@ public final class AppSearchResult<ValueType> implements Parcelable {
return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
}
+ /** @hide */
@Override
public int describeContents() {
return 0;
}
- public static final Creator<AppSearchResult> CREATOR =
- new Creator<AppSearchResult>() {
+ /** @hide */
+ @NonNull
+ public static final Creator<AppSearchResult> CREATOR = new Creator<AppSearchResult>() {
@NonNull
@Override
public AppSearchResult createFromParcel(@NonNull Parcel in) {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 9c7ccea4c43b..b7cd4f5f8bce 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -140,7 +140,11 @@ public final class AppSearchSession {
schemaBundles.add(schema.getBundle());
}
try {
- mService.setSchema(mDatabaseName, schemaBundles, request.isForceOverride(),
+ mService.setSchema(
+ mDatabaseName,
+ schemaBundles,
+ new ArrayList<>(request.getSchemasNotPlatformSurfaceable()),
+ request.isForceOverride(),
new IAppSearchResultCallback.Stub() {
public void onResult(AppSearchResult result) {
executor.execute(() -> callback.accept(result));
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 22e00f2cdfdc..1d7cb87131c0 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -32,6 +32,8 @@ interface IAppSearchManager {
*
* @param databaseName The databaseName this document resides in.
* @param schemaBundles List of AppSearchSchema bundles.
+ * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
+ * surfaces.
* @param forceOverride Whether to apply the new schema even if it is incompatible. All
* incompatible documents will be deleted.
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
@@ -40,6 +42,7 @@ interface IAppSearchManager {
void setSchema(
in String databaseName,
in List<Bundle> schemaBundles,
+ in List<String> schemasNotPlatformSurfaceable,
boolean forceOverride,
in IAppSearchResultCallback callback);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
deleted file mode 100644
index 3e472fd01939..000000000000
--- a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch;
-
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.app.appsearch.exceptions.AppSearchException;
-import android.util.ArraySet;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Encapsulates a request to update the schema of an {@link AppSearchSession} database.
- *
- * @see AppSearchSession#setSchema
- * @hide
- */
-public final class SetSchemaRequest {
- private final Set<AppSearchSchema> mSchemas;
- private final boolean mForceOverride;
-
- SetSchemaRequest(Set<AppSearchSchema> schemas, boolean forceOverride) {
- mSchemas = schemas;
- mForceOverride = forceOverride;
- }
-
- /** Returns the schemas that are part of this request. */
- @NonNull
- public Set<AppSearchSchema> getSchemas() {
- return mSchemas;
- }
-
- /** Returns whether this request will force the schema to be overridden. */
- public boolean isForceOverride() {
- return mForceOverride;
- }
-
- /** Builder for {@link SetSchemaRequest} objects. */
- public static final class Builder {
- private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
- private boolean mForceOverride = false;
- private boolean mBuilt = false;
-
- /** Adds one or more types to the schema. */
- @NonNull
- public Builder addSchema(@NonNull AppSearchSchema... schemas) {
- Preconditions.checkNotNull(schemas);
- return addSchema(Arrays.asList(schemas));
- }
-
- /** Adds one or more types to the schema. */
- @NonNull
- public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schemas);
- mSchemas.addAll(schemas);
- return this;
- }
-
- /**
- * Configures the {@link SetSchemaRequest} to delete any existing documents that don't
- * follow the new schema.
- *
- * <p>By default, this is {@code false} and schema incompatibility causes the {@link
- * AppSearchSession#setSchema} call to fail.
- *
- * @see AppSearchSession#setSchema
- */
- @NonNull
- public Builder setForceOverride(boolean forceOverride) {
- mForceOverride = forceOverride;
- return this;
- }
-
- /** Builds a new {@link SetSchemaRequest}. */
- @NonNull
- public SetSchemaRequest build() {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- mBuilt = true;
- return new SetSchemaRequest(mSchemas, mForceOverride);
- }
- }
-}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
index 9ca363ed9008..9ca363ed9008 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 2db74a8fb798..62cf38bca44d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.IllegalSchemaException;
+import android.app.appsearch.util.BundleUtil;
import android.os.Bundle;
import android.util.ArraySet;
@@ -31,6 +32,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -40,7 +42,7 @@ import java.util.Set;
*
* <p>The schema consists of type information, properties, and config (like tokenization type).
*
- * @see AppSearchManager#setSchema
+ * @see AppSearchSession#setSchema
*/
public final class AppSearchSchema {
private static final String SCHEMA_TYPE_FIELD = "schemaType";
@@ -94,17 +96,37 @@ public final class AppSearchSchema {
return ret;
}
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof AppSearchSchema)) {
+ return false;
+ }
+ AppSearchSchema otherSchema = (AppSearchSchema) other;
+ if (!getSchemaType().equals(otherSchema.getSchemaType())) {
+ return false;
+ }
+ return getProperties().equals(otherSchema.getProperties());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getSchemaType(), getProperties());
+ }
+
/** Builder for {@link AppSearchSchema objects}. */
public static final class Builder {
- private final String mTypeName;
+ private final String mSchemaType;
private final ArrayList<Bundle> mPropertyBundles = new ArrayList<>();
private final Set<String> mPropertyNames = new ArraySet<>();
private boolean mBuilt = false;
/** Creates a new {@link AppSearchSchema.Builder}. */
- public Builder(@NonNull String typeName) {
- Preconditions.checkNotNull(typeName);
- mTypeName = typeName;
+ public Builder(@NonNull String schemaType) {
+ Preconditions.checkNotNull(schemaType);
+ mSchemaType = schemaType;
}
/** Adds a property to the given type. */
@@ -133,7 +155,7 @@ public final class AppSearchSchema {
public AppSearchSchema build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Bundle bundle = new Bundle();
- bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName);
+ bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mSchemaType);
bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles);
mBuilt = true;
return new AppSearchSchema(bundle);
@@ -279,6 +301,8 @@ public final class AppSearchSchema {
final Bundle mBundle;
+ @Nullable private Integer mHashCode;
+
PropertyConfig(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle);
}
@@ -327,6 +351,26 @@ public final class AppSearchSchema {
return mBundle.getInt(TOKENIZER_TYPE_FIELD);
}
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof PropertyConfig)) {
+ return false;
+ }
+ PropertyConfig otherProperty = (PropertyConfig) other;
+ return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle);
+ }
+
+ @Override
+ public int hashCode() {
+ if (mHashCode == null) {
+ mHashCode = BundleUtil.deepHashCode(mBundle);
+ }
+ return mHashCode;
+ }
+
/**
* Builder for {@link PropertyConfig}.
*
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 0056377f007a..85207f7ed9d7 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.AppSearchException;
+import android.app.appsearch.util.BundleUtil;
import android.os.Bundle;
import android.util.Log;
@@ -37,12 +38,12 @@ import java.util.Set;
*
* <p>Documents are constructed via {@link GenericDocument.Builder}.
*
- * @see AppSearchManager#putDocuments
- * @see AppSearchManager#getByUri
- * @see AppSearchManager#query
+ * @see AppSearchSession#putDocuments
+ * @see AppSearchSession#getByUri
+ * @see AppSearchSession#query
*/
public class GenericDocument {
- private static final String TAG = "GenericDocument";
+ private static final String TAG = "AppSearchGenericDocumen";
/** The default empty namespace. */
public static final String DEFAULT_NAMESPACE = "";
@@ -462,144 +463,17 @@ public class GenericDocument {
return false;
}
GenericDocument otherDocument = (GenericDocument) other;
- return bundleEquals(this.mBundle, otherDocument.mBundle);
- }
-
- /**
- * Deeply checks whether two bundles are equal.
- *
- * <p>Two bundles will be considered equal if they contain the same content.
- */
- @SuppressWarnings("unchecked")
- private static boolean bundleEquals(Bundle one, Bundle two) {
- if (one.size() != two.size()) {
- return false;
- }
- Set<String> keySetOne = one.keySet();
- Object valueOne;
- Object valueTwo;
- // Bundle inherit its equals() from Object.java, which only compare their memory address.
- // We should iterate all keys and check their presents and values in both bundle.
- for (String key : keySetOne) {
- valueOne = one.get(key);
- valueTwo = two.get(key);
- if (valueOne instanceof Bundle
- && valueTwo instanceof Bundle
- && !bundleEquals((Bundle) valueOne, (Bundle) valueTwo)) {
- return false;
- } else if (valueOne == null && (valueTwo != null || !two.containsKey(key))) {
- // If we call bundle.get(key) when the 'key' doesn't actually exist in the
- // bundle, we'll get back a null. So make sure that both values are null and
- // both keys exist in the bundle.
- return false;
- } else if (valueOne instanceof boolean[]) {
- if (!(valueTwo instanceof boolean[])
- || !Arrays.equals((boolean[]) valueOne, (boolean[]) valueTwo)) {
- return false;
- }
- } else if (valueOne instanceof long[]) {
- if (!(valueTwo instanceof long[])
- || !Arrays.equals((long[]) valueOne, (long[]) valueTwo)) {
- return false;
- }
- } else if (valueOne instanceof double[]) {
- if (!(valueTwo instanceof double[])
- || !Arrays.equals((double[]) valueOne, (double[]) valueTwo)) {
- return false;
- }
- } else if (valueOne instanceof Bundle[]) {
- if (!(valueTwo instanceof Bundle[])) {
- return false;
- }
- Bundle[] bundlesOne = (Bundle[]) valueOne;
- Bundle[] bundlesTwo = (Bundle[]) valueTwo;
- if (bundlesOne.length != bundlesTwo.length) {
- return false;
- }
- for (int i = 0; i < bundlesOne.length; i++) {
- if (!bundleEquals(bundlesOne[i], bundlesTwo[i])) {
- return false;
- }
- }
- } else if (valueOne instanceof ArrayList) {
- if (!(valueTwo instanceof ArrayList)) {
- return false;
- }
- ArrayList<Bundle> bundlesOne = (ArrayList<Bundle>) valueOne;
- ArrayList<Bundle> bundlesTwo = (ArrayList<Bundle>) valueTwo;
- if (bundlesOne.size() != bundlesTwo.size()) {
- return false;
- }
- for (int i = 0; i < bundlesOne.size(); i++) {
- if (!bundleEquals(bundlesOne.get(i), bundlesTwo.get(i))) {
- return false;
- }
- }
- } else if (valueOne instanceof Object[]) {
- if (!(valueTwo instanceof Object[])
- || !Arrays.equals((Object[]) valueOne, (Object[]) valueTwo)) {
- return false;
- }
- }
- }
- return true;
+ return BundleUtil.deepEquals(this.mBundle, otherDocument.mBundle);
}
@Override
public int hashCode() {
if (mHashCode == null) {
- mHashCode = bundleHashCode(mBundle);
+ mHashCode = BundleUtil.deepHashCode(mBundle);
}
return mHashCode;
}
- /**
- * Calculates the hash code for a bundle.
- *
- * <p>The hash code is only effected by the contents in the bundle. Bundles will get consistent
- * hash code if they have same contents.
- */
- @SuppressWarnings("unchecked")
- private static int bundleHashCode(Bundle bundle) {
- int[] hashCodes = new int[bundle.size()];
- int i = 0;
- // Bundle inherit its hashCode() from Object.java, which only relative to their memory
- // address. Bundle doesn't have an order, so we should iterate all keys and combine
- // their value's hashcode into an array. And use the hashcode of the array to be
- // the hashcode of the bundle.
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
- if (value instanceof boolean[]) {
- hashCodes[i++] = Arrays.hashCode((boolean[]) value);
- } else if (value instanceof long[]) {
- hashCodes[i++] = Arrays.hashCode((long[]) value);
- } else if (value instanceof double[]) {
- hashCodes[i++] = Arrays.hashCode((double[]) value);
- } else if (value instanceof String[]) {
- hashCodes[i++] = Arrays.hashCode((Object[]) value);
- } else if (value instanceof Bundle) {
- hashCodes[i++] = bundleHashCode((Bundle) value);
- } else if (value instanceof Bundle[]) {
- Bundle[] bundles = (Bundle[]) value;
- int[] innerHashCodes = new int[bundles.length];
- for (int j = 0; j < innerHashCodes.length; j++) {
- innerHashCodes[j] = bundleHashCode(bundles[j]);
- }
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
- } else if (value instanceof ArrayList) {
- ArrayList<Bundle> bundles = (ArrayList<Bundle>) value;
- int[] innerHashCodes = new int[bundles.size()];
- for (int j = 0; j < innerHashCodes.length; j++) {
- innerHashCodes[j] = bundleHashCode(bundles.get(j));
- }
- hashCodes[i++] = Arrays.hashCode(innerHashCodes);
- } else {
- hashCodes[i++] = value.hashCode();
- }
- }
- return Arrays.hashCode(hashCodes);
- }
-
@Override
@NonNull
public String toString() {
@@ -683,10 +557,10 @@ public class GenericDocument {
*
* @param uri The uri of {@link GenericDocument}.
* @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@code
- * schemaType} must be defined using {@link AppSearchManager#setSchema} prior to
+ * schemaType} must be defined using {@link AppSearchSession#setSchema} prior to
* inserting a document of this {@code schemaType} into the AppSearch index using {@link
- * AppSearchManager#putDocuments}. Otherwise, the document will be rejected by {@link
- * AppSearchManager#putDocuments}.
+ * AppSearchSession#putDocuments}. Otherwise, the document will be rejected by {@link
+ * AppSearchSession#putDocuments}.
*/
@SuppressWarnings("unchecked")
public Builder(@NonNull String uri, @NonNull String schemaType) {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 053d401d06dc..74afdd2c7a80 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -30,7 +30,6 @@ import java.util.Set;
* Encapsulates a request to retrieve documents by namespace and URI.
*
* @see AppSearchSession#getByUri
- * @hide
*/
public final class GetByUriRequest {
private final String mNamespace;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
index 42f1ff2a716a..1c360a65a041 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
@@ -32,7 +32,6 @@ import java.util.List;
* Encapsulates a request to index a document into an {@link AppSearchSession} database.
*
* @see AppSearchSession#putDocuments
- * @hide
*/
public final class PutDocumentsRequest {
private final List<GenericDocument> mDocuments;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 3d83c390fbad..be6d15708d2e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -30,7 +30,6 @@ import java.util.Set;
* Encapsulates a request to remove documents by namespace and URI.
*
* @see AppSearchSession#removeByUri
- * @hide
*/
public final class RemoveByUriRequest {
private final String mNamespace;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index 5ffa7c94087c..5ffa7c94087c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java
index dbd09d6bb91b..dbd09d6bb91b 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index 68e31f03fb4d..68e31f03fb4d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
new file mode 100644
index 000000000000..0e031312f420
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Encapsulates a request to update the schema of an {@link AppSearchSession} database.
+ *
+ * @see AppSearchSession#setSchema
+ */
+public final class SetSchemaRequest {
+ private final Set<AppSearchSchema> mSchemas;
+ private final Set<String> mSchemasNotPlatformSurfaceable;
+ private final boolean mForceOverride;
+
+ SetSchemaRequest(
+ @NonNull Set<AppSearchSchema> schemas,
+ @NonNull Set<String> schemasNotPlatformSurfaceable,
+ boolean forceOverride) {
+ mSchemas = Preconditions.checkNotNull(schemas);
+ mSchemasNotPlatformSurfaceable = Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
+ mForceOverride = forceOverride;
+ }
+
+ /** Returns the schemas that are part of this request. */
+ @NonNull
+ public Set<AppSearchSchema> getSchemas() {
+ return Collections.unmodifiableSet(mSchemas);
+ }
+
+ /**
+ * Returns the set of schema types that have opted out of being visible on system UI surfaces.
+ *
+ * @hide
+ */
+ @NonNull
+ public Set<String> getSchemasNotPlatformSurfaceable() {
+ return Collections.unmodifiableSet(mSchemasNotPlatformSurfaceable);
+ }
+
+ /** Returns whether this request will force the schema to be overridden. */
+ public boolean isForceOverride() {
+ return mForceOverride;
+ }
+
+ /** Builder for {@link SetSchemaRequest} objects. */
+ public static final class Builder {
+ private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
+ private final Set<String> mSchemasNotPlatformSurfaceable = new ArraySet<>();
+ private boolean mForceOverride = false;
+ private boolean mBuilt = false;
+
+ /**
+ * Adds one or more types to the schema.
+ *
+ * <p>Any documents of these types will be visible on system UI surfaces by default.
+ */
+ @NonNull
+ public Builder addSchema(@NonNull AppSearchSchema... schemas) {
+ Preconditions.checkNotNull(schemas);
+ return addSchema(Arrays.asList(schemas));
+ }
+
+ /**
+ * Adds one or more types to the schema.
+ *
+ * <p>Any documents of these types will be visible on system UI surfaces by default.
+ */
+ @NonNull
+ public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(schemas);
+ mSchemas.addAll(schemas);
+ return this;
+ }
+
+ /**
+ * Sets visibility on system UI surfaces for schema types.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setSchemaTypeVisibilityForSystemUi(
+ boolean visible, @NonNull String... schemaTypes) {
+ Preconditions.checkNotNull(schemaTypes);
+ return this.setSchemaTypeVisibilityForSystemUi(visible, Arrays.asList(schemaTypes));
+ }
+
+ /**
+ * Sets visibility on system UI surfaces for schema types.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setSchemaTypeVisibilityForSystemUi(
+ boolean visible, @NonNull Collection<String> schemaTypes) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(schemaTypes);
+ if (visible) {
+ mSchemasNotPlatformSurfaceable.removeAll(schemaTypes);
+ } else {
+ mSchemasNotPlatformSurfaceable.addAll(schemaTypes);
+ }
+ return this;
+ }
+
+ /**
+ * Configures the {@link SetSchemaRequest} to delete any existing documents that don't
+ * follow the new schema.
+ *
+ * <p>By default, this is {@code false} and schema incompatibility causes the {@link
+ * AppSearchSession#setSchema} call to fail.
+ *
+ * @see AppSearchSession#setSchema
+ */
+ @NonNull
+ public Builder setForceOverride(boolean forceOverride) {
+ mForceOverride = forceOverride;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SetSchemaRequest}.
+ *
+ * @throws IllegalArgumentException If schema types were referenced, but the corresponding
+ * {@link AppSearchSchema} was never added.
+ */
+ @NonNull
+ public SetSchemaRequest build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBuilt = true;
+
+ // Verify that any schema types with visibility settings refer to a real schema.
+ // Create a copy because we're going to remove from the set for verification purposes.
+ Set<String> schemasNotPlatformSurfaceableCopy =
+ new ArraySet<>(mSchemasNotPlatformSurfaceable);
+ for (AppSearchSchema schema : mSchemas) {
+ schemasNotPlatformSurfaceableCopy.remove(schema.getSchemaType());
+ }
+ if (!schemasNotPlatformSurfaceableCopy.isEmpty()) {
+ // We still have schema types that weren't seen in our mSchemas set. This means
+ // there wasn't a corresponding AppSearchSchema.
+ throw new IllegalArgumentException(
+ "Schema types "
+ + schemasNotPlatformSurfaceableCopy
+ + " referenced, but were not added.");
+ }
+
+ return new SetSchemaRequest(mSchemas, mSchemasNotPlatformSurfaceable, mForceOverride);
+ }
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
index 704f180bffc4..704f180bffc4 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java
index 5f8da7fe067a..5f8da7fe067a 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java
index 0b5dc2ece6f1..0b5dc2ece6f1 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
new file mode 100644
index 000000000000..1b4d28401ea0
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.util;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Utilities for working with {@link android.os.Bundle}.
+ *
+ * @hide
+ */
+public final class BundleUtil {
+ private BundleUtil() {}
+
+ /**
+ * Deeply checks two bundles are equal or not.
+ *
+ * <p>Two bundles will be considered equal if they contain the same keys, and each value is also
+ * equal. Bundle values are compared using deepEquals.
+ */
+ public static boolean deepEquals(@Nullable Bundle one, @Nullable Bundle two) {
+ if (one == null && two == null) {
+ return true;
+ }
+ if (one == null || two == null) {
+ return false;
+ }
+ if (one.size() != two.size()) {
+ return false;
+ }
+ if (!one.keySet().equals(two.keySet())) {
+ return false;
+ }
+ // Bundle inherit its equals() from Object.java, which only compare their memory address.
+ // We should iterate all keys and check their presents and values in both bundle.
+ for (String key : one.keySet()) {
+ if (!bundleValueEquals(one.get(key), two.get(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Deeply checks whether two values in a Bundle are equal or not.
+ *
+ * <p>Values of type Bundle are compared using {@link #deepEquals}.
+ */
+ private static boolean bundleValueEquals(@Nullable Object one, @Nullable Object two) {
+ if (one == null && two == null) {
+ return true;
+ }
+ if (one == null || two == null) {
+ return false;
+ }
+ if (one.equals(two)) {
+ return true;
+ }
+ if (one instanceof Bundle && two instanceof Bundle) {
+ return deepEquals((Bundle) one, (Bundle) two);
+ } else if (one instanceof int[] && two instanceof int[]) {
+ return Arrays.equals((int[]) one, (int[]) two);
+ } else if (one instanceof byte[] && two instanceof byte[]) {
+ return Arrays.equals((byte[]) one, (byte[]) two);
+ } else if (one instanceof char[] && two instanceof char[]) {
+ return Arrays.equals((char[]) one, (char[]) two);
+ } else if (one instanceof long[] && two instanceof long[]) {
+ return Arrays.equals((long[]) one, (long[]) two);
+ } else if (one instanceof float[] && two instanceof float[]) {
+ return Arrays.equals((float[]) one, (float[]) two);
+ } else if (one instanceof short[] && two instanceof short[]) {
+ return Arrays.equals((short[]) one, (short[]) two);
+ } else if (one instanceof double[] && two instanceof double[]) {
+ return Arrays.equals((double[]) one, (double[]) two);
+ } else if (one instanceof boolean[] && two instanceof boolean[]) {
+ return Arrays.equals((boolean[]) one, (boolean[]) two);
+ } else if (one instanceof Object[] && two instanceof Object[]) {
+ Object[] arrayOne = (Object[]) one;
+ Object[] arrayTwo = (Object[]) two;
+ if (arrayOne.length != arrayTwo.length) {
+ return false;
+ }
+ if (Arrays.equals(arrayOne, arrayTwo)) {
+ return true;
+ }
+ for (int i = 0; i < arrayOne.length; i++) {
+ if (!bundleValueEquals(arrayOne[i], arrayTwo[i])) {
+ return false;
+ }
+ }
+ return true;
+ } else if (one instanceof ArrayList && two instanceof ArrayList) {
+ ArrayList<?> listOne = (ArrayList<?>) one;
+ ArrayList<?> listTwo = (ArrayList<?>) two;
+ if (listOne.size() != listTwo.size()) {
+ return false;
+ }
+ for (int i = 0; i < listOne.size(); i++) {
+ if (!bundleValueEquals(listOne.get(i), listTwo.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ } else if (one instanceof SparseArray && two instanceof SparseArray) {
+ SparseArray<?> arrayOne = (SparseArray<?>) one;
+ SparseArray<?> arrayTwo = (SparseArray<?>) two;
+ if (arrayOne.size() != arrayTwo.size()) {
+ return false;
+ }
+ for (int i = 0; i < arrayOne.size(); i++) {
+ if (arrayOne.keyAt(i) != arrayTwo.keyAt(i)
+ || !bundleValueEquals(arrayOne.valueAt(i), arrayTwo.valueAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Calculates the hash code for a bundle.
+ *
+ * <p>The hash code is only effected by the contents in the bundle. Bundles will get consistent
+ * hash code if they have same contents.
+ */
+ public static int deepHashCode(@Nullable Bundle bundle) {
+ if (bundle == null) {
+ return 0;
+ }
+ int[] hashCodes = new int[bundle.size()];
+ int i = 0;
+ // Bundle inherit its hashCode() from Object.java, which only relative to their memory
+ // address. Bundle doesn't have an order, so we should iterate all keys and combine
+ // their value's hashcode into an array. And use the hashcode of the array to be
+ // the hashcode of the bundle.
+ for (String key : bundle.keySet()) {
+ Object value = bundle.get(key);
+ if (value instanceof Bundle) {
+ hashCodes[i++] = deepHashCode((Bundle) value);
+ } else if (value instanceof int[]) {
+ hashCodes[i++] = Arrays.hashCode((int[]) value);
+ } else if (value instanceof byte[]) {
+ hashCodes[i++] = Arrays.hashCode((byte[]) value);
+ } else if (value instanceof char[]) {
+ hashCodes[i++] = Arrays.hashCode((char[]) value);
+ } else if (value instanceof long[]) {
+ hashCodes[i++] = Arrays.hashCode((long[]) value);
+ } else if (value instanceof float[]) {
+ hashCodes[i++] = Arrays.hashCode((float[]) value);
+ } else if (value instanceof short[]) {
+ hashCodes[i++] = Arrays.hashCode((short[]) value);
+ } else if (value instanceof double[]) {
+ hashCodes[i++] = Arrays.hashCode((double[]) value);
+ } else if (value instanceof boolean[]) {
+ hashCodes[i++] = Arrays.hashCode((boolean[]) value);
+ } else if (value instanceof String[]) {
+ // Optimization to avoid Object[] handler creating an inner array for common cases
+ hashCodes[i++] = Arrays.hashCode((String[]) value);
+ } else if (value instanceof Object[]) {
+ Object[] array = (Object[]) value;
+ int[] innerHashCodes = new int[array.length];
+ for (int j = 0; j < array.length; j++) {
+ if (array[j] instanceof Bundle) {
+ innerHashCodes[j] = deepHashCode((Bundle) array[j]);
+ } else if (array[j] != null) {
+ innerHashCodes[j] = array[j].hashCode();
+ }
+ }
+ hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ } else if (value instanceof ArrayList) {
+ ArrayList<?> list = (ArrayList<?>) value;
+ int[] innerHashCodes = new int[list.size()];
+ for (int j = 0; j < innerHashCodes.length; j++) {
+ Object item = list.get(j);
+ if (item instanceof Bundle) {
+ innerHashCodes[j] = deepHashCode((Bundle) item);
+ } else if (item != null) {
+ innerHashCodes[j] = item.hashCode();
+ }
+ }
+ hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ } else if (value instanceof SparseArray) {
+ SparseArray<?> array = (SparseArray<?>) value;
+ int[] innerHashCodes = new int[array.size() * 2];
+ for (int j = 0; j < array.size(); j++) {
+ innerHashCodes[j * 2] = array.keyAt(j);
+ Object item = array.valueAt(j);
+ if (item instanceof Bundle) {
+ innerHashCodes[j * 2 + 1] = deepHashCode((Bundle) item);
+ } else if (item != null) {
+ innerHashCodes[j * 2 + 1] = item.hashCode();
+ }
+ }
+ hashCodes[i++] = Arrays.hashCode(innerHashCodes);
+ } else {
+ hashCodes[i++] = value.hashCode();
+ }
+ }
+ return Arrays.hashCode(hashCodes);
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index d5146dd75c3b..551347c5c202 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -33,15 +33,14 @@ import android.os.Bundle;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.ArraySet;
import android.util.Log;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
/**
* TODO(b/142567528): add comments when implement this class
@@ -64,6 +63,7 @@ public class AppSearchManagerService extends SystemService {
public void setSchema(
@NonNull String databaseName,
@NonNull List<Bundle> schemaBundles,
+ @NonNull List<String> schemasNotPlatformSurfaceable,
boolean forceOverride,
@NonNull IAppSearchResultCallback callback) {
Preconditions.checkNotNull(databaseName);
@@ -73,13 +73,13 @@ public class AppSearchManagerService extends SystemService {
int callingUserId = UserHandle.getUserId(callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
- Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size());
+ List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
for (int i = 0; i < schemaBundles.size(); i++) {
schemas.add(new AppSearchSchema(schemaBundles.get(i)));
}
AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
- impl.setSchema(databaseName, schemas, forceOverride);
+ impl.setSchema(databaseName, schemas, schemasNotPlatformSurfaceable, forceOverride);
invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(/*result=*/ null));
} catch (Throwable t) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 247089ba258b..62b81d066c8b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -60,6 +60,7 @@ import com.google.android.icing.proto.SetSchemaResultProto;
import com.google.android.icing.proto.StatusProto;
import java.io.File;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -226,13 +227,16 @@ public final class AppSearchImpl {
*
* @param databaseName The name of the database where this schema lives.
* @param schemas Schemas to set for this app.
+ * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
+ * surfaces.
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
* which do not comply with the new schema will be deleted.
* @throws AppSearchException on IcingSearchEngine error.
*/
public void setSchema(
@NonNull String databaseName,
- @NonNull Set<AppSearchSchema> schemas,
+ @NonNull List<AppSearchSchema> schemas,
+ @NonNull List<String> schemasNotPlatformSurfaceable,
boolean forceOverride)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
@@ -240,8 +244,9 @@ public final class AppSearchImpl {
SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder();
SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
- for (AppSearchSchema schema : schemas) {
- SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema);
+ for (int i = 0; i < schemas.size(); i++) {
+ SchemaTypeConfigProto schemaTypeProto =
+ SchemaToProtoConverter.toSchemaTypeConfigProto(schemas.get(i));
newSchemaBuilder.addTypes(schemaTypeProto);
}
@@ -276,8 +281,16 @@ public final class AppSearchImpl {
// Update derived data structures.
mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes);
- mVisibilityStoreLocked.updateSchemas(
- databaseName, rewrittenSchemaResults.mDeletedQualifiedTypes);
+
+ String databasePrefix = getDatabasePrefix(databaseName);
+ Set<String> qualifiedSchemasNotPlatformSurfaceable =
+ new ArraySet<>(schemasNotPlatformSurfaceable.size());
+ for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) {
+ qualifiedSchemasNotPlatformSurfaceable.add(
+ databasePrefix + schemasNotPlatformSurfaceable.get(i));
+ }
+ mVisibilityStoreLocked.setVisibility(
+ databaseName, qualifiedSchemasNotPlatformSurfaceable);
// Determine whether to schedule an immediate optimize.
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
@@ -294,38 +307,55 @@ public final class AppSearchImpl {
}
/**
- * Update the visibility settings for this app.
+ * Retrieves the AppSearch schema for this database.
*
- * <p>This method belongs to the mutate group
+ * <p>This method belongs to query group.
*
- * @param databaseName The name of the database where the visibility settings will apply.
- * @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform surfaces
- * @throws AppSearchException on IcingSearchEngine error
+ * @param databaseName The name of the database where this schema lives.
+ * @throws AppSearchException on IcingSearchEngine error.
*/
- public void setVisibility(
- @NonNull String databaseName, @NonNull Set<String> schemasHiddenFromPlatformSurfaces)
- throws AppSearchException {
- mReadWriteLock.writeLock().lock();
+ @NonNull
+ public List<AppSearchSchema> getSchema(@NonNull String databaseName) throws AppSearchException {
+ SchemaProto fullSchema;
+ mReadWriteLock.readLock().lock();
try {
- String databasePrefix = getDatabasePrefix(databaseName);
- Set<String> qualifiedSchemasHiddenFromPlatformSurface =
- new ArraySet<>(schemasHiddenFromPlatformSurfaces.size());
- for (String schema : schemasHiddenFromPlatformSurfaces) {
- Set<String> existingSchemas = mSchemaMapLocked.get(databaseName);
- if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) {
- throw new AppSearchException(
- AppSearchResult.RESULT_NOT_FOUND,
- "Unknown schema(s): "
- + schemasHiddenFromPlatformSurfaces
- + " provided during setVisibility.");
+ fullSchema = getSchemaProtoLocked();
+ } finally {
+ mReadWriteLock.readLock().unlock();
+ }
+
+ List<AppSearchSchema> result = new ArrayList<>();
+ for (int i = 0; i < fullSchema.getTypesCount(); i++) {
+ String typeDatabase = getDatabaseName(fullSchema.getTypes(i).getSchemaType());
+ if (!databaseName.equals(typeDatabase)) {
+ continue;
+ }
+ // Rewrite SchemaProto.types.schema_type
+ SchemaTypeConfigProto.Builder typeConfigBuilder = fullSchema.getTypes(i).toBuilder();
+ String newSchemaType =
+ typeConfigBuilder.getSchemaType().substring(databaseName.length() + 1);
+ typeConfigBuilder.setSchemaType(newSchemaType);
+
+ // Rewrite SchemaProto.types.properties.schema_type
+ for (int propertyIdx = 0;
+ propertyIdx < typeConfigBuilder.getPropertiesCount();
+ propertyIdx++) {
+ PropertyConfigProto.Builder propertyConfigBuilder =
+ typeConfigBuilder.getProperties(propertyIdx).toBuilder();
+ if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
+ String newPropertySchemaType =
+ propertyConfigBuilder
+ .getSchemaType()
+ .substring(databaseName.length() + 1);
+ propertyConfigBuilder.setSchemaType(newPropertySchemaType);
+ typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
}
- qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema);
}
- mVisibilityStoreLocked.setVisibility(
- databaseName, qualifiedSchemasHiddenFromPlatformSurface);
- } finally {
- mReadWriteLock.writeLock().lock();
+
+ AppSearchSchema schema = SchemaToProtoConverter.toAppSearchSchema(typeConfigBuilder);
+ result.add(schema);
}
+ return result;
}
/**
@@ -340,7 +370,7 @@ public final class AppSearchImpl {
public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document)
throws AppSearchException {
DocumentProto.Builder documentBuilder =
- GenericDocumentToProtoConverter.convert(document).toBuilder();
+ GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder();
addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName));
PutResultProto putResultProto;
@@ -384,7 +414,7 @@ public final class AppSearchImpl {
DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder();
removeDatabasesFromDocument(documentBuilder);
- return GenericDocumentToProtoConverter.convert(documentBuilder.build());
+ return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build());
}
/**
@@ -969,7 +999,7 @@ public final class AppSearchImpl {
resultsBuilder.setResults(i, resultBuilder);
}
}
- return SearchResultToProtoConverter.convertToSearchResultPage(resultsBuilder);
+ return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder);
}
@GuardedBy("mReadWriteLock")
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
index 47228221a1f5..0b68ebcbfd3f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
@@ -53,19 +53,23 @@ import java.util.Set;
class VisibilityStore {
// Schema type for documents that hold AppSearch's metadata, e.g. visibility settings
@VisibleForTesting static final String SCHEMA_TYPE = "Visibility";
+
// Property that holds the list of platform-hidden schemas, as part of the visibility
// settings.
- @VisibleForTesting static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden";
+ @VisibleForTesting
+ static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
// Database name to prefix all visibility schemas and documents with. Special-cased to
// minimize the chance of collision with a client-supplied database.
+
@VisibleForTesting static final String DATABASE_NAME = "$$__AppSearch__Database";
+
// Namespace of documents that contain visibility settings
private static final String NAMESPACE = "namespace";
private final AppSearchImpl mAppSearchImpl;
// The map contains schemas that are platform-hidden for each database. All schemas in the map
// have a database name prefix.
- private final Map<String, Set<String>> mPlatformHiddenMap = new ArrayMap<>();
+ private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>();
/**
* Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
@@ -82,8 +86,8 @@ class VisibilityStore {
*
* <p>This is kept separate from the constructor because this will call methods on
* AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example,
- * {@link AppSearchImpl#setSchema} will call {@link #updateSchemas}. We need to have both
- * AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
+ * {@link AppSearchImpl#setSchema} will call {@link #setVisibility(String, Set)}. We need to
+ * have both AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
*
* @throws AppSearchException AppSearchException on AppSearchImpl error.
*/
@@ -92,11 +96,11 @@ class VisibilityStore {
// Schema type doesn't exist yet. Add it.
mAppSearchImpl.setSchema(
DATABASE_NAME,
- Collections.singleton(
+ Collections.singletonList(
new AppSearchSchema.Builder(SCHEMA_TYPE)
.addProperty(
new AppSearchSchema.PropertyConfig.Builder(
- PLATFORM_HIDDEN_PROPERTY)
+ NOT_PLATFORM_SURFACEABLE_PROPERTY)
.setDataType(
AppSearchSchema.PropertyConfig
.DATA_TYPE_STRING)
@@ -105,6 +109,7 @@ class VisibilityStore {
.CARDINALITY_REPEATED)
.build())
.build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
}
@@ -120,8 +125,9 @@ class VisibilityStore {
GenericDocument document =
mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ database);
- String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
- mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas)));
+ String[] schemas =
+ document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
+ mNotPlatformSurfaceableMap.put(database, new ArraySet<>(Arrays.asList(schemas)));
} catch (AppSearchException e) {
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
// TODO(b/172068212): This indicates some desync error. We were expecting a
@@ -136,94 +142,33 @@ class VisibilityStore {
}
/**
- * Update visibility settings for the {@code databaseName}.
- *
- * @param schemasToRemove Database-prefixed schemas that should be removed
- */
- public void updateSchemas(@NonNull String databaseName, @NonNull Set<String> schemasToRemove)
- throws AppSearchException {
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(schemasToRemove);
-
- GenericDocument visibilityDocument;
- try {
- visibilityDocument =
- mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName);
- } catch (AppSearchException e) {
- if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
- // This might be the first time we're seeing visibility changes for a database.
- // Create a new visibility document.
- mAppSearchImpl.putDocument(
- DATABASE_NAME,
- new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
- .setNamespace(NAMESPACE)
- .build());
-
- // Since we know there was nothing that existed before, we don't need to remove
- // anything either. Return early.
- return;
- }
- // Otherwise, this is some real error we should pass up.
- throw e;
- }
-
- String[] hiddenSchemas =
- visibilityDocument.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY);
- if (hiddenSchemas == null) {
- // Nothing to remove.
- return;
- }
-
- // Create a new set so we can remove from it.
- Set<String> remainingSchemas = new ArraySet<>(Arrays.asList(hiddenSchemas));
- boolean changed = remainingSchemas.removeAll(schemasToRemove);
- if (!changed) {
- // Nothing was actually removed. Can return early.
- return;
- }
-
- // Update our persisted document
- // TODO(b/171882200): Switch to a .toBuilder API when it's available.
- GenericDocument.Builder newVisibilityDocument =
- new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
- .setNamespace(NAMESPACE);
- if (!remainingSchemas.isEmpty()) {
- newVisibilityDocument.setPropertyString(
- PLATFORM_HIDDEN_PROPERTY, remainingSchemas.toArray(new String[0]));
- }
- mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build());
-
- // Update derived data structures
- mPlatformHiddenMap.put(databaseName, remainingSchemas);
- }
-
- /**
* Sets visibility settings for {@code databaseName}. Any previous visibility settings will be
* overwritten.
*
- * @param databaseName Database name that owns the {@code platformHiddenSchemas}.
- * @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from the
- * platform.
+ * @param databaseName Database name that owns the {@code schemasNotPlatformSurfaceable}.
+ * @param schemasNotPlatformSurfaceable Set of database-qualified schemas that should be hidden
+ * from the platform.
* @throws AppSearchException on AppSearchImpl error.
*/
public void setVisibility(
- @NonNull String databaseName, @NonNull Set<String> platformHiddenSchemas)
+ @NonNull String databaseName, @NonNull Set<String> schemasNotPlatformSurfaceable)
throws AppSearchException {
Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(platformHiddenSchemas);
+ Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
// Persist the document
GenericDocument.Builder visibilityDocument =
new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE)
.setNamespace(NAMESPACE);
- if (!platformHiddenSchemas.isEmpty()) {
+ if (!schemasNotPlatformSurfaceable.isEmpty()) {
visibilityDocument.setPropertyString(
- PLATFORM_HIDDEN_PROPERTY, platformHiddenSchemas.toArray(new String[0]));
+ NOT_PLATFORM_SURFACEABLE_PROPERTY,
+ schemasNotPlatformSurfaceable.toArray(new String[0]));
}
mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build());
// Update derived data structures.
- mPlatformHiddenMap.put(databaseName, platformHiddenSchemas);
+ mNotPlatformSurfaceableMap.put(databaseName, schemasNotPlatformSurfaceable);
}
/**
@@ -235,13 +180,13 @@ class VisibilityStore {
* none exist.
*/
@NonNull
- public Set<String> getPlatformHiddenSchemas(@NonNull String databaseName) {
+ public Set<String> getSchemasNotPlatformSurfaceable(@NonNull String databaseName) {
Preconditions.checkNotNull(databaseName);
- Set<String> platformHiddenSchemas = mPlatformHiddenMap.get(databaseName);
- if (platformHiddenSchemas == null) {
+ Set<String> schemasNotPlatformSurfaceable = mNotPlatformSurfaceableMap.get(databaseName);
+ if (schemasNotPlatformSurfaceable == null) {
return Collections.emptySet();
}
- return platformHiddenSchemas;
+ return schemasNotPlatformSurfaceable;
}
/**
@@ -251,7 +196,7 @@ class VisibilityStore {
* @throws AppSearchException on AppSearchImpl error.
*/
public void handleReset() throws AppSearchException {
- mPlatformHiddenMap.clear();
+ mNotPlatformSurfaceableMap.clear();
initialize();
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
index 8f4e7ff69d7c..5474cd04287c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -39,7 +39,7 @@ public final class GenericDocumentToProtoConverter {
/** Converts a {@link GenericDocument} into a {@link DocumentProto}. */
@NonNull
@SuppressWarnings("unchecked")
- public static DocumentProto convert(@NonNull GenericDocument document) {
+ public static DocumentProto toDocumentProto(@NonNull GenericDocument document) {
Preconditions.checkNotNull(document);
DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
mProtoBuilder
@@ -82,7 +82,7 @@ public final class GenericDocumentToProtoConverter {
}
} else if (documentValues != null) {
for (int j = 0; j < documentValues.length; j++) {
- DocumentProto proto = convert(documentValues[j]);
+ DocumentProto proto = toDocumentProto(documentValues[j]);
propertyProto.addDocumentValues(proto);
}
} else {
@@ -96,7 +96,7 @@ public final class GenericDocumentToProtoConverter {
/** Converts a {@link DocumentProto} into a {@link GenericDocument}. */
@NonNull
- public static GenericDocument convert(@NonNull DocumentProto proto) {
+ public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) {
Preconditions.checkNotNull(proto);
GenericDocument.Builder<?> documentBuilder =
new GenericDocument.Builder<>(proto.getUri(), proto.getSchema())
@@ -141,7 +141,7 @@ public final class GenericDocumentToProtoConverter {
} else if (property.getDocumentValuesCount() > 0) {
GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()];
for (int j = 0; j < values.length; j++) {
- values[j] = convert(property.getDocumentValues(j));
+ values[j] = toGenericDocument(property.getDocumentValues(j));
}
documentBuilder.setPropertyDocument(name, values);
} else {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
index 642c2a713930..4165af31c00b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
@@ -18,11 +18,13 @@ package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchSchema;
+import android.util.Log;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.SchemaTypeConfigProto;
+import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder;
import com.google.android.icing.proto.StringIndexingConfig;
import com.google.android.icing.proto.TermMatchType;
@@ -34,6 +36,8 @@ import java.util.List;
* @hide
*/
public final class SchemaToProtoConverter {
+ private static final String TAG = "AppSearchSchemaToProtoC";
+
private SchemaToProtoConverter() {}
/**
@@ -41,23 +45,23 @@ public final class SchemaToProtoConverter {
* SchemaTypeConfigProto}.
*/
@NonNull
- public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) {
+ public static SchemaTypeConfigProto toSchemaTypeConfigProto(@NonNull AppSearchSchema schema) {
Preconditions.checkNotNull(schema);
SchemaTypeConfigProto.Builder protoBuilder =
SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType());
List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
for (int i = 0; i < properties.size(); i++) {
- PropertyConfigProto propertyProto = convertProperty(properties.get(i));
+ PropertyConfigProto propertyProto = toPropertyConfigProto(properties.get(i));
protoBuilder.addProperties(propertyProto);
}
return protoBuilder.build();
}
@NonNull
- private static PropertyConfigProto convertProperty(
+ private static PropertyConfigProto toPropertyConfigProto(
@NonNull AppSearchSchema.PropertyConfig property) {
Preconditions.checkNotNull(property);
- PropertyConfigProto.Builder propertyConfigProto =
+ PropertyConfigProto.Builder builder =
PropertyConfigProto.newBuilder().setPropertyName(property.getName());
StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder();
@@ -68,12 +72,12 @@ public final class SchemaToProtoConverter {
if (dataTypeProto == null) {
throw new IllegalArgumentException("Invalid dataType: " + dataType);
}
- propertyConfigProto.setDataType(dataTypeProto);
+ builder.setDataType(dataTypeProto);
// Set schemaType
String schemaType = property.getSchemaType();
if (schemaType != null) {
- propertyConfigProto.setSchemaType(schemaType);
+ builder.setSchemaType(schemaType);
}
// Set cardinality
@@ -83,7 +87,7 @@ public final class SchemaToProtoConverter {
if (cardinalityProto == null) {
throw new IllegalArgumentException("Invalid cardinality: " + dataType);
}
- propertyConfigProto.setCardinality(cardinalityProto);
+ builder.setCardinality(cardinalityProto);
// Set indexingType
@AppSearchSchema.PropertyConfig.IndexingType int indexingType = property.getIndexingType();
@@ -114,7 +118,63 @@ public final class SchemaToProtoConverter {
indexingConfig.setTokenizerType(tokenizerTypeProto);
// Build!
- propertyConfigProto.setStringIndexingConfig(indexingConfig);
- return propertyConfigProto.build();
+ builder.setStringIndexingConfig(indexingConfig);
+ return builder.build();
+ }
+
+ /**
+ * Converts a {@link SchemaTypeConfigProto} into an {@link
+ * android.app.appsearch.AppSearchSchema}.
+ */
+ @NonNull
+ public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) {
+ Preconditions.checkNotNull(proto);
+ AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType());
+ List<PropertyConfigProto> properties = proto.getPropertiesList();
+ for (int i = 0; i < properties.size(); i++) {
+ AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i));
+ builder.addProperty(propertyConfig);
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ private static AppSearchSchema.PropertyConfig toPropertyConfig(
+ @NonNull PropertyConfigProto proto) {
+ Preconditions.checkNotNull(proto);
+ AppSearchSchema.PropertyConfig.Builder builder =
+ new AppSearchSchema.PropertyConfig.Builder(proto.getPropertyName())
+ .setDataType(proto.getDataType().getNumber())
+ .setCardinality(proto.getCardinality().getNumber())
+ .setTokenizerType(
+ proto.getStringIndexingConfig().getTokenizerType().getNumber());
+
+ // Set schema
+ if (!proto.getSchemaType().isEmpty()) {
+ builder.setSchemaType(proto.getSchemaType());
+ }
+
+ // Set indexingType
+ @AppSearchSchema.PropertyConfig.IndexingType int indexingType;
+ TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType();
+ switch (termMatchTypeProto) {
+ case UNKNOWN:
+ indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
+ break;
+ case EXACT_ONLY:
+ indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS;
+ break;
+ case PREFIX:
+ indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES;
+ break;
+ default:
+ // Avoid crashing in the 'read' path; we should try to interpret the document to the
+ // extent possible.
+ Log.w(TAG, "Invalid indexingType: " + termMatchTypeProto.getNumber());
+ indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
+ }
+ builder.setIndexingType(indexingType);
+
+ return builder.build();
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index b91a393cab4a..4d107a970abd 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -39,13 +39,12 @@ public class SearchResultToProtoConverter {
/** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */
@NonNull
- public static SearchResultPage convertToSearchResultPage(
- @NonNull SearchResultProtoOrBuilder proto) {
+ public static SearchResultPage toSearchResultPage(@NonNull SearchResultProtoOrBuilder proto) {
Bundle bundle = new Bundle();
bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken());
ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount());
for (int i = 0; i < proto.getResultsCount(); i++) {
- resultBundles.add(convertToSearchResultBundle(proto.getResults(i)));
+ resultBundles.add(toSearchResultBundle(proto.getResults(i)));
}
bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
return new SearchResultPage(bundle);
@@ -53,10 +52,11 @@ public class SearchResultToProtoConverter {
/** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */
@NonNull
- private static Bundle convertToSearchResultBundle(
+ private static Bundle toSearchResultBundle(
@NonNull SearchResultProto.ResultProtoOrBuilder proto) {
Bundle bundle = new Bundle();
- GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument());
+ GenericDocument document =
+ GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument());
bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle());
ArrayList<Bundle> matchList = new ArrayList<>();
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index a2bf0d504a1f..57c48d838808 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I2decd83fab4c4d58fe38c9970f804046479c942c
+If5fd2bd705d5507d044706701a94b2e1496ef1df
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 3597c27224bb..d16217d5cb41 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -274,13 +274,20 @@ public class JobInfo implements Parcelable {
/**
* This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
- * can set it. Jobs with a time constrant must not have it.
+ * can set it. Jobs with a time constraint must not have it.
*
* @hide
*/
public static final int FLAG_EXEMPT_FROM_APP_STANDBY = 1 << 3;
/**
+ * Whether it's a so-called "HPJ" or not.
+ *
+ * @hide
+ */
+ public static final int FLAG_FOREGROUND_JOB = 1 << 4;
+
+ /**
* @hide
*/
public static final int CONSTRAINT_FLAG_CHARGING = 1 << 0;
@@ -571,6 +578,7 @@ public class JobInfo implements Parcelable {
/**
* Return the backoff policy of this job.
+ *
* @see JobInfo.Builder#setBackoffCriteria(long, int)
*/
public @BackoffPolicy int getBackoffPolicy() {
@@ -578,6 +586,13 @@ public class JobInfo implements Parcelable {
}
/**
+ * @see JobInfo.Builder#setForeground(boolean)
+ */
+ public boolean isForegroundJob() {
+ return (flags & FLAG_FOREGROUND_JOB) != 0;
+ }
+
+ /**
* @see JobInfo.Builder#setImportantWhileForeground(boolean)
*/
public boolean isImportantWhileForeground() {
@@ -1442,6 +1457,41 @@ public class JobInfo implements Parcelable {
}
/**
+ * Setting this to true indicates that this job is important and needs to run as soon as
+ * possible with stronger guarantees than regular jobs. These "foreground" jobs will:
+ * <ol>
+ * <li>Run as soon as possible</li>
+ * <li>Be exempted from Doze and battery saver restrictions</li>
+ * <li>Have network access</li>
+ * </ol>
+ *
+ * Since these jobs have stronger guarantees than regular jobs, they will be subject to
+ * stricter quotas. As long as an app has available foreground quota, jobs scheduled with
+ * this set to true will run with these guarantees. If an app has run out of available
+ * foreground quota, any pending foreground jobs will run as regular jobs.
+ * {@link JobParameters#isForegroundJob()} can be used to know whether the executing job
+ * has foreground guarantees or not. In addition, {@link JobScheduler#schedule(JobInfo)}
+ * will immediately return {@link JobScheduler#RESULT_FAILURE} if the app does not have
+ * available quota (and the job will not be successfully scheduled).
+ *
+ * Foreground jobs may only set network constraints. No other constraints are allowed.
+ *
+ * Note: Even though foreground jobs are meant to run as soon as possible, they may be
+ * deferred if the system is under heavy load or the network constraint is satisfied
+ *
+ * @see JobInfo#isForegroundJob()
+ */
+ @NonNull
+ public Builder setForeground(boolean foreground) {
+ if (foreground) {
+ mFlags |= FLAG_FOREGROUND_JOB;
+ } else {
+ mFlags &= (~FLAG_FOREGROUND_JOB);
+ }
+ return this;
+ }
+
+ /**
* Setting this to true indicates that this job is important while the scheduling app
* is in the foreground or on the temporary whitelist for background restrictions.
* This means that the system will relax doze restrictions on this job during this time.
@@ -1456,7 +1506,9 @@ public class JobInfo implements Parcelable {
* @param importantWhileForeground whether to relax doze restrictions for this job when the
* app is in the foreground. False by default.
* @see JobInfo#isImportantWhileForeground()
+ * @deprecated Use {@link #setForeground(boolean)} instead.
*/
+ @Deprecated
public Builder setImportantWhileForeground(boolean importantWhileForeground) {
if (importantWhileForeground) {
mFlags |= FLAG_IMPORTANT_WHILE_FOREGROUND;
@@ -1580,6 +1632,29 @@ public class JobInfo implements Parcelable {
throw new IllegalArgumentException(
"An important while foreground job cannot have a time delay");
}
+
+ if ((flags & FLAG_FOREGROUND_JOB) != 0) {
+ if (hasEarlyConstraint) {
+ throw new IllegalArgumentException("A foreground job cannot have a time delay");
+ }
+ if (hasLateConstraint) {
+ throw new IllegalArgumentException("A foreground job cannot have a deadline");
+ }
+ if (isPeriodic) {
+ throw new IllegalArgumentException("A foreground job cannot be periodic");
+ }
+ if (isPersisted) {
+ throw new IllegalArgumentException("A foreground job cannot be persisted");
+ }
+ if (constraintFlags != 0 || (flags & ~FLAG_FOREGROUND_JOB) != 0) {
+ throw new IllegalArgumentException(
+ "A foreground job can only have network constraints");
+ }
+ if (triggerContentUris != null && triggerContentUris.length > 0) {
+ throw new IllegalArgumentException(
+ "Can't call addTriggerContentUri() on a foreground job");
+ }
+ }
}
/**
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index e251ff00a47d..a71b8562753e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -111,6 +111,9 @@ public class JobParameters implements Parcelable {
@UnsupportedAppUsage
private final IBinder callback;
private final boolean overrideDeadlineExpired;
+ // HPJs = foreground jobs.
+ // TODO(171305774): clean up naming
+ private final boolean mIsHpj;
private final Uri[] mTriggeredContentUris;
private final String[] mTriggeredContentAuthorities;
private final Network network;
@@ -121,7 +124,7 @@ public class JobParameters implements Parcelable {
/** @hide */
public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
Bundle transientExtras, ClipData clipData, int clipGrantFlags,
- boolean overrideDeadlineExpired, Uri[] triggeredContentUris,
+ boolean overrideDeadlineExpired, boolean isHpj, Uri[] triggeredContentUris,
String[] triggeredContentAuthorities, Network network) {
this.jobId = jobId;
this.extras = extras;
@@ -130,6 +133,7 @@ public class JobParameters implements Parcelable {
this.clipGrantFlags = clipGrantFlags;
this.callback = callback;
this.overrideDeadlineExpired = overrideDeadlineExpired;
+ this.mIsHpj = isHpj;
this.mTriggeredContentUris = triggeredContentUris;
this.mTriggeredContentAuthorities = triggeredContentAuthorities;
this.network = network;
@@ -195,6 +199,17 @@ public class JobParameters implements Parcelable {
}
/**
+ * @return Whether this job is running as a foreground job or not. A job is guaranteed to have
+ * all foreground job guarantees for the duration of the job execution if this returns
+ * {@code true}. This will return {@code false} if the job that wasn't requested to run as a
+ * foreground job, or if it was requested to run as a foreground job but the app didn't have
+ * any remaining foreground job quota at the time of execution.
+ */
+ public boolean isForegroundJob() {
+ return mIsHpj;
+ }
+
+ /**
* For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this
* provides an easy way to tell whether the job is being executed due to the deadline
* expiring. Note: If the job is running because its deadline expired, it implies that its
@@ -337,6 +352,7 @@ public class JobParameters implements Parcelable {
}
callback = in.readStrongBinder();
overrideDeadlineExpired = in.readInt() == 1;
+ mIsHpj = in.readBoolean();
mTriggeredContentUris = in.createTypedArray(Uri.CREATOR);
mTriggeredContentAuthorities = in.createStringArray();
if (in.readInt() != 0) {
@@ -373,6 +389,7 @@ public class JobParameters implements Parcelable {
}
dest.writeStrongBinder(callback);
dest.writeInt(overrideDeadlineExpired ? 1 : 0);
+ dest.writeBoolean(mIsHpj);
dest.writeTypedArray(mTriggeredContentUris, flags);
dest.writeStringArray(mTriggeredContentAuthorities);
if (network != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index b35a7be1f689..0feaade2b51d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -205,7 +205,9 @@ class JobConcurrencyManager {
}
private boolean isFgJob(JobStatus job) {
- return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP;
+ // (It's super confusing PRIORITY_BOUND_FOREGROUND_SERVICE isn't FG here)
+ return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
+ || job.shouldTreatAsForegroundJob();
}
@GuardedBy("mLock")
@@ -336,6 +338,8 @@ class JobConcurrencyManager {
continue;
}
+ // TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them
+
final boolean isPendingFg = isFgJob(nextPending);
// Find an available slot for nextPending. The context should be available OR
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 255e2f6d8779..9ea402c56088 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -834,6 +834,13 @@ public class JobSchedulerService extends com.android.server.SystemService
// Higher override state (OVERRIDE_FULL) should be before lower state (OVERRIDE_SOFT)
return o2.overrideState - o1.overrideState;
}
+ if (o1.getSourceUid() == o2.getSourceUid()) {
+ final boolean o1FGJ = o1.isRequestedForegroundJob();
+ if (o1FGJ != o2.isRequestedForegroundJob()) {
+ // Attempt to run requested HPJs ahead of regular jobs, regardless of HPJ quota.
+ return o1FGJ ? -1 : 1;
+ }
+ }
if (o1.enqueueTime < o2.enqueueTime) {
return -1;
}
@@ -1136,6 +1143,12 @@ public class JobSchedulerService extends com.android.server.SystemService
JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
+ // Return failure early if HPJ quota used up.
+ if (jobStatus.isRequestedForegroundJob()
+ && !mQuotaController.isWithinHpjQuotaLocked(jobStatus)) {
+ return JobScheduler.RESULT_FAILURE;
+ }
+
// Give exemption if the source is in the foreground just now.
// Note if it's a sync job, this method is called on the handler so it's not exactly
// the state when requestSync() was called, but that should be fine because of the
@@ -1791,9 +1804,9 @@ public class JobSchedulerService extends com.android.server.SystemService
* time of the job to be the time of completion (i.e. the time at which this function is
* called).
* <p>This could be inaccurate b/c the job can run for as long as
- * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
- * to underscheduling at least, rather than if we had taken the last execution time to be the
- * start of the execution.
+ * {@link com.android.server.job.JobServiceContext#DEFAULT_EXECUTING_TIMESLICE_MILLIS}, but
+ * will lead to underscheduling at least, rather than if we had taken the last execution time
+ * to be the start of the execution.
*
* @return A new job representing the execution criteria for this instantiation of the
* recurring job.
@@ -1884,6 +1897,10 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
+ // Intentionally not checking HPJ quota here. An app can't find out if it's run out of quota
+ // when it asks JS to reschedule an HPJ. Instead, the rescheduled HPJ will just be demoted
+ // to a regular job if the app has no HPJ quota left.
+
// If the job wants to be rescheduled, we first need to make the next upcoming
// job so we can transfer any appropriate state over from the previous job when
// we stop it.
@@ -2382,7 +2399,7 @@ public class JobSchedulerService extends com.android.server.SystemService
public long getMaxJobExecutionTimeMs(JobStatus job) {
synchronized (mLock) {
return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job),
- JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
+ JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS);
}
}
@@ -2600,7 +2617,6 @@ public class JobSchedulerService extends com.android.server.SystemService
// job that runs one of the app's services, as well as verifying that the
// named service properly requires the BIND_JOB_SERVICE permission
private void enforceValidJobRequest(int uid, JobInfo job) {
- job.enforceValidity();
final PackageManager pm = getContext()
.createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
.getPackageManager();
@@ -2649,6 +2665,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
private void validateJobFlags(JobInfo job, int callingUid) {
+ job.enforceValidity();
if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 565ed959aeb4..5fed1995dfc3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -16,6 +16,7 @@
package com.android.server.job;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.app.job.IJobCallback;
@@ -73,7 +74,13 @@ public final class JobServiceContext implements ServiceConnection {
private static final String TAG = "JobServiceContext";
/** Amount of time a job is allowed to execute for before being considered timed-out. */
- public static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
+ public static final long DEFAULT_EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
+ /**
+ * Amount of time a RESTRICTED HPJ is allowed to execute for before being considered
+ * timed-out.
+ */
+ public static final long DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS =
+ DEFAULT_EXECUTING_TIMESLICE_MILLIS / 2;
/** Amount of time the JobScheduler waits for the initial service launch+bind. */
private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000;
/** Amount of time the JobScheduler will wait for a response from an app for a message. */
@@ -224,7 +231,8 @@ public final class JobServiceContext implements ServiceConnection {
final JobInfo ji = job.getJob();
mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
- isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
+ isDeadlineExpired, job.shouldTreatAsForegroundJob(),
+ triggeredUris, triggeredAuthorities, job.network);
mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
final long whenDeferred = job.getWhenStandbyDeferred();
@@ -250,9 +258,18 @@ public final class JobServiceContext implements ServiceConnection {
final Intent intent = new Intent().setComponent(job.getServiceComponent());
boolean binding = false;
try {
- binding = mContext.bindServiceAsUser(intent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_NOT_PERCEPTIBLE,
+ final int bindFlags;
+ if (job.shouldTreatAsForegroundJob()) {
+ // Add BIND_FOREGROUND_SERVICE to make it BFGS. Without it, it'll be
+ // PROCESS_STATE_IMPORTANT_FOREGROUND. Unclear which is better here.
+ // TODO(171305774): The job should run on the little cores. We'll probably need
+ // another binding flag for that.
+ bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
+ } else {
+ bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_NOT_PERCEPTIBLE;
+ }
+ binding = mContext.bindServiceAsUser(intent, this, bindFlags,
UserHandle.of(job.getUserId()));
} catch (SecurityException e) {
// Some permission policy, for example INTERACT_ACROSS_USERS and
@@ -848,7 +865,10 @@ public final class JobServiceContext implements ServiceConnection {
final long timeoutMillis;
switch (mVerb) {
case VERB_EXECUTING:
- timeoutMillis = EXECUTING_TIMESLICE_MILLIS;
+ timeoutMillis = mRunningJob.shouldTreatAsForegroundJob()
+ && mRunningJob.getStandbyBucket() == RESTRICTED_INDEX
+ ? DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS
+ : DEFAULT_EXECUTING_TIMESLICE_MILLIS;
break;
case VERB_BINDING:
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 8f6c68dad36c..5ec1b8951a7b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -85,6 +85,7 @@ public final class JobStatus {
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
+ static final int CONSTRAINT_WITHIN_HPJ_QUOTA = 1 << 23; // Implicit constraint
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
/**
@@ -376,6 +377,9 @@ public final class JobStatus {
/** The job is within its quota based on its standby bucket. */
private boolean mReadyWithinQuota;
+ /** The job is a foreground job with sufficient quota to run as a foreground job. */
+ private boolean mReadyWithinHpjQuota;
+
/** The job's dynamic requirements have been satisfied. */
private boolean mReadyDynamicSatisfied;
@@ -1036,20 +1040,34 @@ public final class JobStatus {
mPersistedUtcTimes = null;
}
+ /** @return true if the app has requested that this run as a foreground job. */
+ public boolean isRequestedForegroundJob() {
+ return (getFlags() & JobInfo.FLAG_FOREGROUND_JOB) != 0;
+ }
+
+ /**
+ * @return true if all foreground job requirements are satisfied and therefore this should be
+ * treated as a foreground job.
+ */
+ public boolean shouldTreatAsForegroundJob() {
+ return mReadyWithinHpjQuota && isRequestedForegroundJob();
+ }
+
/**
* @return true if the job is exempted from Doze restrictions and therefore allowed to run
* in Doze.
*/
public boolean canRunInDoze() {
- return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+ return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsForegroundJob();
}
boolean canRunInBatterySaver() {
- return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0;
+ return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
+ || shouldTreatAsForegroundJob();
}
boolean shouldIgnoreNetworkBlocking() {
- return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+ return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsForegroundJob();
}
/** @return true if the constraint was changed, false otherwise. */
@@ -1128,6 +1146,16 @@ public final class JobStatus {
return false;
}
+ /** @return true if the constraint was changed, false otherwise. */
+ boolean setForegroundJobQuotaConstraintSatisfied(boolean state) {
+ if (setConstraintSatisfied(CONSTRAINT_WITHIN_HPJ_QUOTA, state)) {
+ // The constraint was changed. Update the ready flag.
+ mReadyWithinHpjQuota = state;
+ return true;
+ }
+ return false;
+ }
+
/** @return true if the state was changed, false otherwise. */
boolean setUidActive(final boolean newActiveState) {
if (newActiveState != uidActive) {
@@ -1257,6 +1285,10 @@ public final class JobStatus {
oldValue = mReadyWithinQuota;
mReadyWithinQuota = true;
break;
+ case CONSTRAINT_WITHIN_HPJ_QUOTA:
+ oldValue = mReadyWithinHpjQuota;
+ mReadyWithinHpjQuota = true;
+ break;
default:
satisfied |= constraint;
mReadyDynamicSatisfied = mDynamicConstraints != 0
@@ -1279,6 +1311,9 @@ public final class JobStatus {
case CONSTRAINT_WITHIN_QUOTA:
mReadyWithinQuota = oldValue;
break;
+ case CONSTRAINT_WITHIN_HPJ_QUOTA:
+ mReadyWithinHpjQuota = oldValue;
+ break;
default:
mReadyDynamicSatisfied = mDynamicConstraints != 0
&& mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
@@ -1293,7 +1328,7 @@ public final class JobStatus {
// sessions (exempt from dynamic restrictions), we need the additional check to ensure
// that NEVER jobs don't run.
// TODO: cleanup quota and standby bucket management so we don't need the additional checks
- if ((!mReadyWithinQuota && !mReadyDynamicSatisfied)
+ if ((!mReadyWithinQuota && !mReadyDynamicSatisfied && !shouldTreatAsForegroundJob())
|| getEffectiveStandbyBucket() == NEVER_INDEX) {
return false;
}
@@ -1506,6 +1541,9 @@ public final class JobStatus {
if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
pw.print(" WITHIN_QUOTA");
}
+ if ((constraints & CONSTRAINT_WITHIN_HPJ_QUOTA) != 0) {
+ pw.print(" WITHIN_HPJ_QUOTA");
+ }
if (constraints != 0) {
pw.print(" [0x");
pw.print(Integer.toHexString(constraints));
@@ -1578,6 +1616,9 @@ public final class JobStatus {
if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
}
+ if ((constraints & CONSTRAINT_WITHIN_HPJ_QUOTA) != 0) {
+ proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_HPJ_QUOTA);
+ }
}
private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
@@ -1795,6 +1836,11 @@ public final class JobStatus {
pw.print(prefix);
pw.print(" readyComponentEnabled: ");
pw.println(serviceInfo != null);
+ if ((getFlags() & JobInfo.FLAG_FOREGROUND_JOB) != 0) {
+ pw.print(prefix);
+ pw.print(" mReadyWithinHpjQuota: ");
+ pw.println(mReadyWithinHpjQuota);
+ }
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 1d72b42d824d..a3a20c6c1091 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -36,6 +36,9 @@ import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.IUidObserver;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -53,6 +56,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
import android.util.SparseSetArray;
@@ -297,10 +301,17 @@ public final class QuotaController extends StateController {
/** Timer for each package-userId combo. */
private final SparseArrayMap<String, Timer> mPkgTimers = new SparseArrayMap<>();
- /** List of all timing sessions for a package-userId combo, in chronological order. */
+ /** Timer for HPJs for each package-userId combo. */
+ private final SparseArrayMap<String, Timer> mHpjPkgTimers = new SparseArrayMap<>();
+
+ /** List of all regular timing sessions for a package-userId combo, in chronological order. */
private final SparseArrayMap<String, List<TimingSession>> mTimingSessions =
new SparseArrayMap<>();
+ /** List of all hpj timing sessions for a package-userId combo, in chronological order. */
+ private final SparseArrayMap<String, List<TimingSession>> mHpjTimingSessions =
+ new SparseArrayMap<>();
+
/**
* Listener to track and manage when each package comes back within quota.
*/
@@ -311,6 +322,10 @@ public final class QuotaController extends StateController {
private final SparseArrayMap<String, ExecutionStats[]> mExecutionStatsCache =
new SparseArrayMap<>();
+ private final SparseArrayMap<String, ShrinkableDebits> mHpjStats = new SparseArrayMap<>();
+
+ private final SparseArrayMap<String, TopAppTimer> mTopAppTrackers = new SparseArrayMap<>();
+
/** List of UIDs currently in the foreground. */
private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
@@ -327,7 +342,7 @@ public final class QuotaController extends StateController {
private final ActivityManagerInternal mActivityManagerInternal;
private final AlarmManager mAlarmManager;
private final ChargingTracker mChargeTracker;
- private final Handler mHandler;
+ private final QcHandler mHandler;
private final QcConstants mQcConstants;
/** How much time each app will have to run jobs within their standby bucket window. */
@@ -474,14 +489,63 @@ public final class QuotaController extends StateController {
private long mTimingSessionCoalescingDurationMs =
QcConstants.DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS;
+ /**
+ * The rolling window size for each standby bucket. Within each window, an app will have 10
+ * minutes to run its jobs.
+ */
+ private final long[] mHpjLimitsMs = new long[]{
+ QcConstants.DEFAULT_HPJ_LIMIT_ACTIVE_MS,
+ QcConstants.DEFAULT_HPJ_LIMIT_WORKING_MS,
+ QcConstants.DEFAULT_HPJ_LIMIT_FREQUENT_MS,
+ QcConstants.DEFAULT_HPJ_LIMIT_RARE_MS,
+ 0, // NEVER
+ QcConstants.DEFAULT_HPJ_LIMIT_RESTRICTED_MS
+ };
+
+ /**
+ * The period of time used to calculate HPJ sessions. Apps can only have HPJ sessions
+ * totalling {@link #mHpjLimitsMs}[bucket within this period of time (without factoring in any
+ * rewards or free HPJs).
+ */
+ private long mHpjLimitWindowSizeMs = QcConstants.DEFAULT_HPJ_WINDOW_SIZE_MS;
+
+ /**
+ * Length of time used to split an app's top time into chunks.
+ */
+ public long mHpjTopAppTimeChunkSizeMs = QcConstants.DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+
+ /**
+ * How much HPJ quota to give back to an app based on the number of top app time chunks it had.
+ */
+ public long mHpjRewardTopAppMs = QcConstants.DEFAULT_HPJ_REWARD_TOP_APP_MS;
+
+ /**
+ * How much HPJ quota to give back to an app based on each non-top user interaction.
+ */
+ public long mHpjRewardInteractionMs = QcConstants.DEFAULT_HPJ_REWARD_INTERACTION_MS;
+
+ /**
+ * How much HPJ quota to give back to an app based on each notification seen event.
+ */
+ public long mHpjRewardNotificationSeenMs = QcConstants.DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS;
+
/** An app has reached its quota. The message should contain a {@link Package} object. */
- private static final int MSG_REACHED_QUOTA = 0;
+ @VisibleForTesting
+ static final int MSG_REACHED_QUOTA = 0;
/** Drop any old timing sessions. */
private static final int MSG_CLEAN_UP_SESSIONS = 1;
/** Check if a package is now within its quota. */
private static final int MSG_CHECK_PACKAGE = 2;
/** Process state for a UID has changed. */
private static final int MSG_UID_PROCESS_STATE_CHANGED = 3;
+ /** An app has reached its HPJ quota. The message should contain a {@link Package} object. */
+ @VisibleForTesting
+ static final int MSG_REACHED_HPJ_QUOTA = 4;
+ /**
+ * Process a new {@link UsageEvents.Event}. The event will be the message's object and the
+ * userId will the first arg.
+ */
+ private static final int MSG_PROCESS_USAGE_EVENT = 5;
public QuotaController(JobSchedulerService service) {
super(service);
@@ -499,6 +563,9 @@ public final class QuotaController extends StateController {
AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
appStandby.addListener(new StandbyTracker());
+ UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class);
+ usmi.registerListener(new UsageEventTracker());
+
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE,
@@ -521,7 +588,15 @@ public final class QuotaController extends StateController {
jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
setConstraintSatisfied(jobStatus, isWithinQuota);
- if (!isWithinQuota) {
+ final boolean outOfHpjQuota;
+ if (jobStatus.isRequestedForegroundJob()) {
+ final boolean isWithinHpjQuota = isWithinHpjQuotaLocked(jobStatus);
+ jobStatus.setForegroundJobQuotaConstraintSatisfied(isWithinHpjQuota);
+ outOfHpjQuota = !isWithinHpjQuota;
+ } else {
+ outOfHpjQuota = false;
+ }
+ if (!isWithinQuota || outOfHpjQuota) {
maybeScheduleStartAlarmLocked(userId, pkgName, jobStatus.getEffectiveStandbyBucket());
}
}
@@ -544,10 +619,12 @@ public final class QuotaController extends StateController {
final int userId = jobStatus.getSourceUserId();
final String packageName = jobStatus.getSourcePackageName();
- Timer timer = mPkgTimers.get(userId, packageName);
+ final SparseArrayMap<String, Timer> timerMap =
+ jobStatus.shouldTreatAsForegroundJob() ? mHpjPkgTimers : mPkgTimers;
+ Timer timer = timerMap.get(userId, packageName);
if (timer == null) {
- timer = new Timer(uid, userId, packageName);
- mPkgTimers.add(userId, packageName, timer);
+ timer = new Timer(uid, userId, packageName, !jobStatus.shouldTreatAsForegroundJob());
+ timerMap.add(userId, packageName, timer);
}
timer.startTrackingJobLocked(jobStatus);
}
@@ -561,6 +638,13 @@ public final class QuotaController extends StateController {
if (timer != null) {
timer.stopTrackingJob(jobStatus);
}
+ if (jobStatus.isRequestedForegroundJob()) {
+ timer = mHpjPkgTimers.get(jobStatus.getSourceUserId(),
+ jobStatus.getSourcePackageName());
+ if (timer != null) {
+ timer.stopTrackingJob(jobStatus);
+ }
+ }
ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
if (jobs != null) {
@@ -585,26 +669,37 @@ public final class QuotaController extends StateController {
public void onUserRemovedLocked(int userId) {
mTrackedJobs.delete(userId);
mPkgTimers.delete(userId);
+ mHpjPkgTimers.delete(userId);
mTimingSessions.delete(userId);
+ mHpjTimingSessions.delete(userId);
mInQuotaAlarmListener.removeAlarmsLocked(userId);
mExecutionStatsCache.delete(userId);
+ mHpjStats.delete(userId);
mUidToPackageCache.clear();
}
/** Drop all historical stats and stop tracking any active sessions for the specified app. */
public void clearAppStatsLocked(int userId, @NonNull String packageName) {
mTrackedJobs.delete(userId, packageName);
- Timer timer = mPkgTimers.get(userId, packageName);
+ Timer timer = mPkgTimers.delete(userId, packageName);
if (timer != null) {
if (timer.isActive()) {
Slog.e(TAG, "clearAppStats called before Timer turned off.");
timer.dropEverythingLocked();
}
- mPkgTimers.delete(userId, packageName);
+ }
+ timer = mHpjPkgTimers.delete(userId, packageName);
+ if (timer != null) {
+ if (timer.isActive()) {
+ Slog.e(TAG, "clearAppStats called before HPJ Timer turned off.");
+ timer.dropEverythingLocked();
+ }
}
mTimingSessions.delete(userId, packageName);
+ mHpjTimingSessions.delete(userId, packageName);
mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
mExecutionStatsCache.delete(userId, packageName);
+ mHpjStats.delete(userId, packageName);
}
private boolean isUidInForeground(int uid) {
@@ -627,11 +722,53 @@ public final class QuotaController extends StateController {
if (mChargeTracker.isCharging()
|| isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())) {
- return JobServiceContext.EXECUTING_TIMESLICE_MILLIS;
+ return JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS;
+ }
+ if (jobStatus.shouldTreatAsForegroundJob()) {
+ return jobStatus.getStandbyBucket() == RESTRICTED_INDEX
+ ? JobServiceContext.DEFAULT_RESTRICTED_HPJ_EXECUTING_TIMESLICE_MILLIS
+ : JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS;
}
return getRemainingExecutionTimeLocked(jobStatus);
}
+ /** @return true if the job is within hpj quota. */
+ public boolean isWithinHpjQuotaLocked(@NonNull final JobStatus jobStatus) {
+ if (isQuotaFree(jobStatus.getEffectiveStandbyBucket())) {
+ return true;
+ }
+ // A job is within quota if one of the following is true:
+ // 1. it's already running (already executing HPJS should be allowed to finish)
+ // 2. the app is currently in the foreground
+ // 3. the app overall is within its quota
+ if (isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) {
+ return true;
+ }
+ Timer hpjTimer = mHpjPkgTimers.get(jobStatus.getSourceUserId(),
+ jobStatus.getSourcePackageName());
+ // Any already executing HPJs should be allowed to finish.
+ if (hpjTimer != null && hpjTimer.isRunning(jobStatus)) {
+ return true;
+ }
+
+ return 0 < getRemainingHpjExecutionTimeLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ }
+
+ @NonNull
+ private ShrinkableDebits getHpjQuotaLocked(final int userId,
+ @NonNull final String packageName) {
+ ShrinkableDebits debits = mHpjStats.get(userId, packageName);
+ if (debits == null) {
+ debits = new ShrinkableDebits(
+ JobSchedulerService.standbyBucketForPackage(
+ packageName, userId, sElapsedRealtimeClock.millis())
+ );
+ mHpjStats.add(userId, packageName, debits);
+ }
+ return debits;
+ }
+
@VisibleForTesting
boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
final int standbyBucket = jobStatus.getEffectiveStandbyBucket();
@@ -645,19 +782,22 @@ public final class QuotaController extends StateController {
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
}
- @VisibleForTesting
- boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
- final int standbyBucket) {
- if (standbyBucket == NEVER_INDEX) return false;
-
+ private boolean isQuotaFree(final int standbyBucket) {
// Quota constraint is not enforced while charging.
if (mChargeTracker.isCharging()) {
// Restricted jobs require additional constraints when charging, so don't immediately
// mark quota as free when charging.
- if (standbyBucket != RESTRICTED_INDEX) {
- return true;
- }
+ return standbyBucket != RESTRICTED_INDEX;
}
+ return false;
+ }
+
+ @VisibleForTesting
+ boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
+ final int standbyBucket) {
+ if (standbyBucket == NEVER_INDEX) return false;
+
+ if (isQuotaFree(standbyBucket)) return true;
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
return getRemainingExecutionTimeLocked(stats) > 0
@@ -717,6 +857,49 @@ public final class QuotaController extends StateController {
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs);
}
+ @VisibleForTesting
+ long getRemainingHpjExecutionTimeLocked(final int userId, @NonNull final String packageName) {
+ ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName);
+ if (quota.getStandbyBucketLocked() == NEVER_INDEX) {
+ return 0;
+ }
+ final long limitMs = mHpjLimitsMs[quota.getStandbyBucketLocked()];
+ long remainingMs = limitMs - quota.getTallyLocked();
+
+ // Stale sessions may still be factored into tally. Make sure they're removed.
+ List<TimingSession> timingSessions = mHpjTimingSessions.get(userId, packageName);
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final long windowStartTimeElapsed = nowElapsed - mHpjLimitWindowSizeMs;
+ if (timingSessions != null) {
+ while (timingSessions.size() > 0) {
+ TimingSession ts = timingSessions.get(0);
+ if (ts.endTimeElapsed < windowStartTimeElapsed) {
+ final long duration = ts.endTimeElapsed - ts.startTimeElapsed;
+ remainingMs += duration;
+ quota.transactOnDebitsLocked(-duration);
+ timingSessions.remove(0);
+ } else if (ts.startTimeElapsed < windowStartTimeElapsed) {
+ remainingMs += windowStartTimeElapsed - ts.startTimeElapsed;
+ break;
+ } else {
+ // Fully within the window.
+ break;
+ }
+ }
+ }
+
+ Timer timer = mHpjPkgTimers.get(userId, packageName);
+ if (timer == null) {
+ return remainingMs;
+ }
+ // There's a case where the debits tally is 0 but a currently running HPJ still counts
+ // towards quota. If the app gets a reward in this case, the reward is lost and the HPJ
+ // run is still fully counted.
+ // TODO(171305774)/STOPSHIP: make sure getting rewards while HPJ currently executing isn't
+ // treated negatively
+ return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis());
+ }
+
/**
* Returns the amount of time, in milliseconds, until the package would have reached its
* duration quota, assuming it has a job counting towards its quota the entire time. This takes
@@ -802,6 +985,61 @@ public final class QuotaController extends StateController {
return timeUntilQuotaConsumedMs;
}
+ /**
+ * Returns the amount of time, in milliseconds, until the package would have reached its HPJ
+ * quota, assuming it has a job counting towards the quota the entire time and the quota isn't
+ * replenished at all in that time.
+ */
+ @VisibleForTesting
+ long getTimeUntilHpjQuotaConsumedLocked(final int userId, @NonNull final String packageName) {
+ final long remainingExecutionTimeMs =
+ getRemainingHpjExecutionTimeLocked(userId, packageName);
+
+ List<TimingSession> sessions = mHpjTimingSessions.get(userId, packageName);
+ if (sessions == null || sessions.size() == 0) {
+ return remainingExecutionTimeMs;
+ }
+
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName);
+ final long limitMs = mHpjLimitsMs[quota.getStandbyBucketLocked()];
+ final long startWindowElapsed = Math.max(0, nowElapsed - mHpjLimitWindowSizeMs);
+ long remainingDeadSpaceMs = remainingExecutionTimeMs;
+ // Total time looked at where a session wouldn't be phasing out.
+ long deadSpaceMs = 0;
+ // Time regained from sessions phasing out
+ long phasedOutSessionTimeMs = 0;
+
+ for (int i = 0; i < sessions.size(); ++i) {
+ TimingSession session = sessions.get(i);
+ if (session.endTimeElapsed < startWindowElapsed) {
+ // Edge case where a session became stale in the time between the call to
+ // getRemainingHpjExecutionTimeLocked and this line.
+ remainingDeadSpaceMs += session.endTimeElapsed - session.startTimeElapsed;
+ sessions.remove(i);
+ i--;
+ } else if (session.startTimeElapsed < startWindowElapsed) {
+ // Session straddles start of window
+ phasedOutSessionTimeMs = session.endTimeElapsed - startWindowElapsed;
+ } else {
+ // Session fully inside window
+ final long timeBetweenSessions = session.startTimeElapsed
+ - (i == 0 ? startWindowElapsed : sessions.get(i - 1).endTimeElapsed);
+ final long usedDeadSpaceMs = Math.min(remainingDeadSpaceMs, timeBetweenSessions);
+ deadSpaceMs += usedDeadSpaceMs;
+ if (usedDeadSpaceMs == timeBetweenSessions) {
+ phasedOutSessionTimeMs += session.endTimeElapsed - session.startTimeElapsed;
+ }
+ remainingDeadSpaceMs -= usedDeadSpaceMs;
+ if (remainingDeadSpaceMs <= 0) {
+ break;
+ }
+ }
+ }
+
+ return Math.min(limitMs, deadSpaceMs + phasedOutSessionTimeMs + remainingDeadSpaceMs);
+ }
+
/** Returns the execution stats of the app in the most recent window. */
@VisibleForTesting
@NonNull
@@ -1053,18 +1291,36 @@ public final class QuotaController extends StateController {
@VisibleForTesting
void saveTimingSession(final int userId, @NonNull final String packageName,
- @NonNull final TimingSession session) {
+ @NonNull final TimingSession session, boolean isHpj) {
synchronized (mLock) {
- List<TimingSession> sessions = mTimingSessions.get(userId, packageName);
+ final SparseArrayMap<String, List<TimingSession>> sessionMap =
+ isHpj ? mHpjTimingSessions : mTimingSessions;
+ List<TimingSession> sessions = sessionMap.get(userId, packageName);
if (sessions == null) {
sessions = new ArrayList<>();
- mTimingSessions.add(userId, packageName, sessions);
+ sessionMap.add(userId, packageName, sessions);
}
sessions.add(session);
- // Adding a new session means that the current stats are now incorrect.
- invalidateAllExecutionStatsLocked(userId, packageName);
+ if (isHpj) {
+ final ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName);
+ quota.transactOnDebitsLocked(session.endTimeElapsed - session.startTimeElapsed);
+ } else {
+ // Adding a new session means that the current stats are now incorrect.
+ invalidateAllExecutionStatsLocked(userId, packageName);
- maybeScheduleCleanupAlarmLocked();
+ maybeScheduleCleanupAlarmLocked();
+ }
+ }
+ }
+
+ private void grantRewardForInstantEvent(
+ final int userId, @NonNull final String packageName, final long credit) {
+ synchronized (mLock) {
+ final ShrinkableDebits quota = getHpjQuotaLocked(userId, packageName);
+ quota.transactOnDebitsLocked(-credit);
+ if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
+ mStateChangedListener.onControllerStateChanged();
+ }
}
}
@@ -1100,6 +1356,7 @@ public final class QuotaController extends StateController {
}
mEarliestEndTimeFunctor.reset();
mTimingSessions.forEach(mEarliestEndTimeFunctor);
+ mHpjTimingSessions.forEach(mEarliestEndTimeFunctor);
final long earliestEndElapsed = mEarliestEndTimeFunctor.earliestEndElapsed;
if (earliestEndElapsed == Long.MAX_VALUE) {
// Couldn't find a good time to clean up. Maybe this was called after we deleted all
@@ -1155,6 +1412,7 @@ public final class QuotaController extends StateController {
Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isCharging());
}
// Deal with Timers first.
+ mHpjPkgTimers.forEach(mTimerChargingUpdateFunctor);
mPkgTimers.forEach(mTimerChargingUpdateFunctor);
// Now update jobs.
maybeUpdateAllConstraintsLocked();
@@ -1189,6 +1447,7 @@ public final class QuotaController extends StateController {
// Quota is the same for all jobs within a package.
final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket();
final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket);
+ boolean outOfHpjQuota = false;
boolean changed = false;
for (int i = jobs.size() - 1; i >= 0; --i) {
final JobStatus js = jobs.valueAt(i);
@@ -1206,8 +1465,14 @@ public final class QuotaController extends StateController {
// This job is somehow exempted. Need to determine its own quota status.
changed |= setConstraintSatisfied(js, isWithinQuotaLocked(js));
}
+
+ if (js.isRequestedForegroundJob()) {
+ boolean isWithinHpjQuota = isWithinHpjQuotaLocked(js);
+ changed |= js.setForegroundJobQuotaConstraintSatisfied(isWithinHpjQuota);
+ outOfHpjQuota |= !isWithinHpjQuota;
+ }
}
- if (!realInQuota) {
+ if (!realInQuota || outOfHpjQuota) {
// Don't want to use the effective standby bucket here since that bump the bucket to
// ACTIVE for one of the jobs, which doesn't help with other jobs that aren't
// exempted.
@@ -1226,10 +1491,22 @@ public final class QuotaController extends StateController {
@Override
public void accept(JobStatus jobStatus) {
wasJobChanged |= setConstraintSatisfied(jobStatus, isWithinQuotaLocked(jobStatus));
+ final boolean outOfHpjQuota;
+ if (jobStatus.isRequestedForegroundJob()) {
+ final boolean isWithinHpjQuota = isWithinHpjQuotaLocked(jobStatus);
+ wasJobChanged |= jobStatus.setForegroundJobQuotaConstraintSatisfied(
+ isWithinHpjQuota);
+ outOfHpjQuota = !isWithinHpjQuota;
+ } else {
+ outOfHpjQuota = false;
+ }
+
final int userId = jobStatus.getSourceUserId();
final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket();
- if (isWithinQuotaLocked(userId, packageName, realStandbyBucket)) {
+ if (isWithinQuotaLocked(userId, packageName, realStandbyBucket) && !outOfHpjQuota) {
+ // TODO(141645789): we probably shouldn't cancel the alarm until we've verified
+ // that all jobs for the userId-package are within quota.
mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
} else {
mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
@@ -1280,11 +1557,13 @@ public final class QuotaController extends StateController {
final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats,
standbyBucket);
+ final long remainingHpjQuota = getRemainingHpjExecutionTimeLocked(userId, packageName);
if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
&& isUnderJobCountQuota
- && isUnderTimingSessionCountQuota) {
+ && isUnderTimingSessionCountQuota
+ && remainingHpjQuota > 0) {
// Already in quota. Why was this method called?
if (DEBUG) {
Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
@@ -1297,19 +1576,44 @@ public final class QuotaController extends StateController {
return;
}
- // The time this app will have quota again.
- long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
- if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
- // App hit the rate limit.
- inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
- stats.jobRateLimitExpirationTimeElapsed);
- }
- if (!isUnderTimingSessionCountQuota
- && stats.sessionCountInWindow < stats.sessionCountLimit) {
- // App hit the rate limit.
- inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
- stats.sessionRateLimitExpirationTimeElapsed);
+ long inRegularQuotaTimeElapsed = Long.MAX_VALUE;
+ long inHpjQuotaTimeElapsed = Long.MAX_VALUE;
+ if (!(stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
+ && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
+ && isUnderJobCountQuota
+ && isUnderTimingSessionCountQuota)) {
+ // The time this app will have quota again.
+ long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
+ if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
+ // App hit the rate limit.
+ inQuotaTimeElapsed =
+ Math.max(inQuotaTimeElapsed, stats.jobRateLimitExpirationTimeElapsed);
+ }
+ if (!isUnderTimingSessionCountQuota
+ && stats.sessionCountInWindow < stats.sessionCountLimit) {
+ // App hit the rate limit.
+ inQuotaTimeElapsed =
+ Math.max(inQuotaTimeElapsed, stats.sessionRateLimitExpirationTimeElapsed);
+ }
+ inRegularQuotaTimeElapsed = inQuotaTimeElapsed;
+ }
+ if (remainingHpjQuota <= 0) {
+ final long limitMs = mHpjLimitsMs[standbyBucket] - mQuotaBufferMs;
+ List<TimingSession> timingSessions = mHpjTimingSessions.get(userId, packageName);
+ long sumMs = 0;
+ for (int i = timingSessions.size() - 1; i >= 0; --i) {
+ TimingSession ts = timingSessions.get(i);
+ final long durationMs = ts.endTimeElapsed - ts.startTimeElapsed;
+ sumMs += durationMs;
+ if (sumMs >= limitMs) {
+ inHpjQuotaTimeElapsed =
+ ts.startTimeElapsed + (sumMs - limitMs) + mHpjLimitWindowSizeMs;
+ break;
+ }
+ }
}
+ long inQuotaTimeElapsed = Math.min(inRegularQuotaTimeElapsed, inHpjQuotaTimeElapsed);
+
if (inQuotaTimeElapsed <= sElapsedRealtimeClock.millis()) {
final long nowElapsed = sElapsedRealtimeClock.millis();
Slog.wtf(TAG,
@@ -1450,9 +1754,52 @@ public final class QuotaController extends StateController {
}
}
+ private static final class ShrinkableDebits {
+ /** The amount of quota remaining. Can be negative if limit changes. */
+ private long mDebitTally;
+ private int mStandbyBucket;
+
+ ShrinkableDebits(int standbyBucket) {
+ mDebitTally = 0;
+ mStandbyBucket = standbyBucket;
+ }
+
+ long getTallyLocked() {
+ return mDebitTally;
+ }
+
+ /**
+ * Negative if the tally should decrease (therefore increasing available quota);
+ * or positive if the tally should increase (therefore decreasing available quota).
+ */
+ void transactOnDebitsLocked(final long amount) {
+ mDebitTally = Math.max(0, mDebitTally + amount);
+ }
+
+ void setStandbyBucketLocked(int standbyBucket) {
+ mStandbyBucket = standbyBucket;
+ }
+
+ int getStandbyBucketLocked() {
+ return mStandbyBucket;
+ }
+
+ @Override
+ public String toString() {
+ return "ShrinkableDebits { debit tally: "
+ + mDebitTally + ", bucket: " + mStandbyBucket
+ + " }";
+ }
+
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.println(toString());
+ }
+ }
+
private final class Timer {
private final Package mPkg;
private final int mUid;
+ private final boolean mRegularJobTimer;
// List of jobs currently running for this app that started when the app wasn't in the
// foreground.
@@ -1460,9 +1807,10 @@ public final class QuotaController extends StateController {
private long mStartTimeElapsed;
private int mBgJobCount;
- Timer(int uid, int userId, String packageName) {
+ Timer(int uid, int userId, String packageName, boolean regularJobTimer) {
mPkg = new Package(userId, packageName);
mUid = uid;
+ mRegularJobTimer = regularJobTimer;
}
void startTrackingJobLocked(@NonNull JobStatus jobStatus) {
@@ -1482,12 +1830,17 @@ public final class QuotaController extends StateController {
mRunningBgJobs.add(jobStatus);
if (shouldTrackLocked()) {
mBgJobCount++;
- incrementJobCountLocked(mPkg.userId, mPkg.packageName, 1);
+ if (mRegularJobTimer) {
+ incrementJobCountLocked(mPkg.userId, mPkg.packageName, 1);
+ }
if (mRunningBgJobs.size() == 1) {
// Started tracking the first job.
mStartTimeElapsed = sElapsedRealtimeClock.millis();
- // Starting the timer means that all cached execution stats are now incorrect.
- invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
+ if (mRegularJobTimer) {
+ // Starting the timer means that all cached execution stats are now
+ // incorrect.
+ invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
+ }
scheduleCutoff();
}
}
@@ -1529,13 +1882,15 @@ public final class QuotaController extends StateController {
return;
}
TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mBgJobCount);
- saveTimingSession(mPkg.userId, mPkg.packageName, ts);
+ saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer);
mBgJobCount = 0;
// Don't reset the tracked jobs list as we need to keep tracking the current number
// of jobs.
// However, cancel the currently scheduled cutoff since it's not currently useful.
cancelCutoff();
- incrementTimingSessionCountLocked(mPkg.userId, mPkg.packageName);
+ if (mRegularJobTimer) {
+ incrementTimingSessionCountLocked(mPkg.userId, mPkg.packageName);
+ }
}
/**
@@ -1582,10 +1937,13 @@ public final class QuotaController extends StateController {
// repeatedly plugged in and unplugged, or an app changes foreground state
// very frequently, the job count for a package may be artificially high.
mBgJobCount = mRunningBgJobs.size();
- incrementJobCountLocked(mPkg.userId, mPkg.packageName, mBgJobCount);
- // Starting the timer means that all cached execution stats are now
- // incorrect.
- invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
+
+ if (mRegularJobTimer) {
+ incrementJobCountLocked(mPkg.userId, mPkg.packageName, mBgJobCount);
+ // Starting the timer means that all cached execution stats are now
+ // incorrect.
+ invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
+ }
// Schedule cutoff since we're now actively tracking for quotas again.
scheduleCutoff();
}
@@ -1605,11 +1963,15 @@ public final class QuotaController extends StateController {
if (!isActive()) {
return;
}
- Message msg = mHandler.obtainMessage(MSG_REACHED_QUOTA, mPkg);
- final long timeRemainingMs = getTimeUntilQuotaConsumedLocked(mPkg.userId,
- mPkg.packageName);
+ Message msg = mHandler.obtainMessage(
+ mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_HPJ_QUOTA, mPkg);
+ final long timeRemainingMs = mRegularJobTimer
+ ? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName)
+ : getTimeUntilHpjQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
if (DEBUG) {
- Slog.i(TAG, "Job for " + mPkg + " has " + timeRemainingMs + "ms left.");
+ Slog.i(TAG,
+ (mRegularJobTimer ? "Regular job" : "HPJ") + " for " + mPkg + " has "
+ + timeRemainingMs + "ms left.");
}
// If the job was running the entire time, then the system would be up, so it's
// fine to use uptime millis for these messages.
@@ -1618,11 +1980,14 @@ public final class QuotaController extends StateController {
}
private void cancelCutoff() {
- mHandler.removeMessages(MSG_REACHED_QUOTA, mPkg);
+ mHandler.removeMessages(
+ mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_HPJ_QUOTA, mPkg);
}
public void dump(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
- pw.print("Timer{");
+ pw.print("Timer<");
+ pw.print(mRegularJobTimer ? "REG" : " HPJ");
+ pw.print(">{");
pw.print(mPkg);
pw.print("} ");
if (isActive()) {
@@ -1668,6 +2033,98 @@ public final class QuotaController extends StateController {
}
}
+ private final class TopAppTimer {
+ private final Package mPkg;
+
+ // List of jobs currently running for this app that started when the app wasn't in the
+ // foreground.
+ private final SparseArray<UsageEvents.Event> mActivities = new SparseArray<>();
+ private long mStartTimeElapsed;
+
+ TopAppTimer(int userId, String packageName) {
+ mPkg = new Package(userId, packageName);
+ }
+
+ void processEventLocked(@NonNull UsageEvents.Event event) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ switch (event.getEventType()) {
+ case UsageEvents.Event.ACTIVITY_RESUMED:
+ if (mActivities.size() == 0) {
+ mStartTimeElapsed = nowElapsed;
+ }
+ mActivities.put(event.mInstanceId, event);
+ break;
+ case UsageEvents.Event.ACTIVITY_PAUSED:
+ case UsageEvents.Event.ACTIVITY_STOPPED:
+ case UsageEvents.Event.ACTIVITY_DESTROYED:
+ final UsageEvents.Event existingEvent =
+ mActivities.removeReturnOld(event.mInstanceId);
+ if (existingEvent != null && mActivities.size() == 0) {
+ final long totalTopTimeMs = nowElapsed - mStartTimeElapsed;
+ int numTimeChunks = (int) (totalTopTimeMs / mHpjTopAppTimeChunkSizeMs);
+ final long remainderMs = totalTopTimeMs % mHpjTopAppTimeChunkSizeMs;
+ if (remainderMs >= SECOND_IN_MILLIS) {
+ // "Round up"
+ numTimeChunks++;
+ }
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Crediting " + mPkg + " for " + numTimeChunks + " time chunks");
+ }
+ final ShrinkableDebits quota =
+ getHpjQuotaLocked(mPkg.userId, mPkg.packageName);
+ quota.transactOnDebitsLocked(-mHpjRewardTopAppMs * numTimeChunks);
+ if (maybeUpdateConstraintForPkgLocked(mPkg.userId, mPkg.packageName)) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+ break;
+ }
+ }
+
+ boolean isActive() {
+ synchronized (mLock) {
+ return mActivities.size() > 0;
+ }
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.print("TopAppTimer{");
+ pw.print(mPkg);
+ pw.print("} ");
+ if (isActive()) {
+ pw.print("started at ");
+ pw.print(mStartTimeElapsed);
+ pw.print(" (");
+ pw.print(sElapsedRealtimeClock.millis() - mStartTimeElapsed);
+ pw.print("ms ago)");
+ } else {
+ pw.print("NOT active");
+ }
+ pw.println();
+ pw.increaseIndent();
+ for (int i = 0; i < mActivities.size(); i++) {
+ UsageEvents.Event event = mActivities.valueAt(i);
+ pw.println(event.getClassName());
+ }
+ pw.decreaseIndent();
+ }
+
+ public void dump(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ mPkg.dumpDebug(proto, StateControllerProto.QuotaController.TopAppTimer.PKG);
+ proto.write(StateControllerProto.QuotaController.TopAppTimer.IS_ACTIVE, isActive());
+ proto.write(StateControllerProto.QuotaController.TopAppTimer.START_TIME_ELAPSED,
+ mStartTimeElapsed);
+ proto.write(StateControllerProto.QuotaController.TopAppTimer.ACTIVITY_COUNT,
+ mActivities.size());
+ // TODO: maybe dump activities/events
+
+ proto.end(token);
+ }
+ }
+
/**
* Tracking of app assignments to standby buckets
*/
@@ -1693,8 +2150,14 @@ public final class QuotaController extends StateController {
}
List<JobStatus> restrictedChanges = new ArrayList<>();
synchronized (mLock) {
+ ShrinkableDebits debits = mHpjStats.get(userId, packageName);
+ if (debits != null) {
+ debits.setStandbyBucketLocked(bucketIndex);
+ }
+
ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
if (jobs == null || jobs.size() == 0) {
+ // Nothing further to do.
return;
}
for (int i = jobs.size() - 1; i >= 0; i--) {
@@ -1711,6 +2174,10 @@ public final class QuotaController extends StateController {
if (timer != null && timer.isActive()) {
timer.rescheduleCutoff();
}
+ timer = mHpjPkgTimers.get(userId, packageName);
+ if (timer != null && timer.isActive()) {
+ timer.rescheduleCutoff();
+ }
if (maybeUpdateConstraintForPkgLocked(userId, packageName)) {
mStateChangedListener.onControllerStateChanged();
}
@@ -1720,6 +2187,16 @@ public final class QuotaController extends StateController {
}
}
+ final class UsageEventTracker implements UsageEventListener {
+ /**
+ * Callback to inform listeners of a new event.
+ */
+ @Override
+ public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
+ mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event);
+ }
+ }
+
private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> {
private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() {
public boolean test(TimingSession ts) {
@@ -1742,9 +2219,13 @@ public final class QuotaController extends StateController {
@VisibleForTesting
void deleteObsoleteSessionsLocked() {
mTimingSessions.forEach(mDeleteOldSessionsFunctor);
+ // Don't delete HPJ timing sessions here. They'll be removed in
+ // getRemainingHpjExecutionTimeLocked().
}
private class QcHandler extends Handler {
+ private boolean mIsProcessing;
+
QcHandler(Looper looper) {
super(looper);
}
@@ -1752,6 +2233,8 @@ public final class QuotaController extends StateController {
@Override
public void handleMessage(Message msg) {
synchronized (mLock) {
+ mIsProcessing = true;
+
switch (msg.what) {
case MSG_REACHED_QUOTA: {
Package pkg = (Package) msg.obj;
@@ -1781,6 +2264,33 @@ public final class QuotaController extends StateController {
}
break;
}
+ case MSG_REACHED_HPJ_QUOTA: {
+ Package pkg = (Package) msg.obj;
+ if (DEBUG) {
+ Slog.d(TAG, "Checking if " + pkg + " has reached its HPJ quota.");
+ }
+
+ long timeRemainingMs = getRemainingHpjExecutionTimeLocked(
+ pkg.userId, pkg.packageName);
+ if (timeRemainingMs <= 0) {
+ if (DEBUG) Slog.d(TAG, pkg + " has reached its HPJ quota.");
+ if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ } else {
+ // This could potentially happen if an old session phases out while a
+ // job is currently running.
+ // Reschedule message
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_HPJ_QUOTA, pkg);
+ timeRemainingMs = getTimeUntilHpjQuotaConsumedLocked(
+ pkg.userId, pkg.packageName);
+ if (DEBUG) {
+ Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left for HPJ");
+ }
+ sendMessageDelayed(rescheduleMsg, timeRemainingMs);
+ }
+ break;
+ }
case MSG_CLEAN_UP_SESSIONS:
if (DEBUG) {
Slog.d(TAG, "Cleaning up timing sessions.");
@@ -1816,7 +2326,8 @@ public final class QuotaController extends StateController {
isQuotaFree = false;
}
// Update Timers first.
- if (mPkgTimers.indexOfKey(userId) >= 0) {
+ if (mPkgTimers.indexOfKey(userId) >= 0
+ || mHpjPkgTimers.indexOfKey(userId) >= 0) {
ArraySet<String> packages = mUidToPackageCache.get(uid);
if (packages == null) {
try {
@@ -1834,7 +2345,11 @@ public final class QuotaController extends StateController {
}
if (packages != null) {
for (int i = packages.size() - 1; i >= 0; --i) {
- Timer t = mPkgTimers.get(userId, packages.valueAt(i));
+ Timer t = mHpjPkgTimers.get(userId, packages.valueAt(i));
+ if (t != null) {
+ t.onStateChangedLocked(nowElapsed, isQuotaFree);
+ }
+ t = mPkgTimers.get(userId, packages.valueAt(i));
if (t != null) {
t.onStateChangedLocked(nowElapsed, isQuotaFree);
}
@@ -1847,8 +2362,46 @@ public final class QuotaController extends StateController {
}
break;
}
+ case MSG_PROCESS_USAGE_EVENT: {
+ final int userId = msg.arg1;
+ final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
+ final String pkgName = event.getPackageName();
+ switch (event.getEventType()) {
+ case UsageEvents.Event.ACTIVITY_RESUMED:
+ case UsageEvents.Event.ACTIVITY_PAUSED:
+ case UsageEvents.Event.ACTIVITY_STOPPED:
+ case UsageEvents.Event.ACTIVITY_DESTROYED:
+ synchronized (mLock) {
+ TopAppTimer timer = mTopAppTrackers.get(userId, pkgName);
+ if (timer == null) {
+ timer = new TopAppTimer(userId, pkgName);
+ mTopAppTrackers.add(userId, pkgName, timer);
+ }
+ timer.processEventLocked(event);
+ }
+ break;
+ case UsageEvents.Event.USER_INTERACTION:
+ case UsageEvents.Event.CHOOSER_ACTION:
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ // Don't need to include SHORTCUT_INVOCATION. The app will be
+ // launched through it (if it's not already on top).
+ grantRewardForInstantEvent(
+ userId, pkgName, mHpjRewardInteractionMs);
+ break;
+ case UsageEvents.Event.NOTIFICATION_SEEN:
+ // Intentionally don't give too much for notification seen.
+ // Interactions will award more.
+ grantRewardForInstantEvent(
+ userId, pkgName, mHpjRewardNotificationSeenMs);
+ break;
+ }
+
+ break;
+ }
}
}
+
+ mIsProcessing = false;
}
}
@@ -2030,6 +2583,7 @@ public final class QuotaController extends StateController {
mQcConstants.mShouldReevaluateConstraints = false;
mQcConstants.mRateLimitingConstantsUpdated = false;
mQcConstants.mExecutionPeriodConstantsUpdated = false;
+ mQcConstants.mHpjLimitConstantsUpdated = false;
}
@Override
@@ -2055,6 +2609,7 @@ public final class QuotaController extends StateController {
private boolean mShouldReevaluateConstraints = false;
private boolean mRateLimitingConstantsUpdated = false;
private boolean mExecutionPeriodConstantsUpdated = false;
+ private boolean mHpjLimitConstantsUpdated = false;
/** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
private static final String QC_CONSTANT_PREFIX = "qc_";
@@ -2128,6 +2683,36 @@ public final class QuotaController extends StateController {
@VisibleForTesting
static final String KEY_MIN_QUOTA_CHECK_DELAY_MS =
QC_CONSTANT_PREFIX + "min_quota_check_delay_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_LIMIT_ACTIVE_MS =
+ QC_CONSTANT_PREFIX + "hpj_limit_active_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_LIMIT_WORKING_MS =
+ QC_CONSTANT_PREFIX + "hpj_limit_working_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_LIMIT_FREQUENT_MS =
+ QC_CONSTANT_PREFIX + "hpj_limit_frequent_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_LIMIT_RARE_MS =
+ QC_CONSTANT_PREFIX + "hpj_limit_rare_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_LIMIT_RESTRICTED_MS =
+ QC_CONSTANT_PREFIX + "hpj_limit_restricted_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_WINDOW_SIZE_MS =
+ QC_CONSTANT_PREFIX + "hpj_window_size_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS =
+ QC_CONSTANT_PREFIX + "hpj_top_app_time_chunk_size_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_REWARD_TOP_APP_MS =
+ QC_CONSTANT_PREFIX + "hpj_reward_top_app_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_REWARD_INTERACTION_MS =
+ QC_CONSTANT_PREFIX + "hpj_reward_interaction_ms";
+ @VisibleForTesting
+ static final String KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS =
+ QC_CONSTANT_PREFIX + "hpj_reward_notification_seen_ms";
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
10 * 60 * 1000L; // 10 minutes
@@ -2169,6 +2754,16 @@ public final class QuotaController extends StateController {
private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
private static final long DEFAULT_MIN_QUOTA_CHECK_DELAY_MS = MINUTE_IN_MILLIS;
+ private static final long DEFAULT_HPJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS;
+ private static final long DEFAULT_HPJ_LIMIT_WORKING_MS = DEFAULT_HPJ_LIMIT_ACTIVE_MS;
+ private static final long DEFAULT_HPJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS;
+ private static final long DEFAULT_HPJ_LIMIT_RARE_MS = DEFAULT_HPJ_LIMIT_FREQUENT_MS;
+ private static final long DEFAULT_HPJ_LIMIT_RESTRICTED_MS = 5 * MINUTE_IN_MILLIS;
+ private static final long DEFAULT_HPJ_WINDOW_SIZE_MS = 24 * HOUR_IN_MILLIS;
+ private static final long DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS = 30 * SECOND_IN_MILLIS;
+ private static final long DEFAULT_HPJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS;
+ private static final long DEFAULT_HPJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS;
+ private static final long DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS = 0;
/** How much time each app will have to run jobs within their standby bucket window. */
public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
@@ -2328,6 +2923,74 @@ public final class QuotaController extends StateController {
/** The minimum value that {@link #RATE_LIMITING_WINDOW_MS} can have. */
private static final long MIN_RATE_LIMITING_WINDOW_MS = 30 * SECOND_IN_MILLIS;
+ /**
+ * The total session limit of the particular standby bucket. Apps in this standby bucket
+ * can
+ * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ * HPJs).
+ */
+ public long HPJ_LIMIT_ACTIVE_MS = DEFAULT_HPJ_LIMIT_ACTIVE_MS;
+
+ /**
+ * The total session limit of the particular standby bucket. Apps in this standby bucket
+ * can
+ * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ * HPJs).
+ */
+ public long HPJ_LIMIT_WORKING_MS = DEFAULT_HPJ_LIMIT_WORKING_MS;
+
+ /**
+ * The total session limit of the particular standby bucket. Apps in this standby bucket
+ * can
+ * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ * HPJs).
+ */
+ public long HPJ_LIMIT_FREQUENT_MS = DEFAULT_HPJ_LIMIT_FREQUENT_MS;
+
+ /**
+ * The total session limit of the particular standby bucket. Apps in this standby bucket
+ * can
+ * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ * HPJs).
+ */
+ public long HPJ_LIMIT_RARE_MS = DEFAULT_HPJ_LIMIT_RARE_MS;
+
+ /**
+ * The total session limit of the particular standby bucket. Apps in this standby bucket
+ * can
+ * only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ * HPJs).
+ */
+ public long HPJ_LIMIT_RESTRICTED_MS = DEFAULT_HPJ_LIMIT_RESTRICTED_MS;
+
+ /**
+ * The period of time used to calculate HPJ sessions. Apps can only have HPJ sessions
+ * totalling HPJ_LIMIT_<bucket>_MS within this period of time (without factoring in any
+ * rewards or free HPJs).
+ */
+ public long HPJ_WINDOW_SIZE_MS = DEFAULT_HPJ_WINDOW_SIZE_MS;
+
+ /**
+ * Length of time used to split an app's top time into chunks.
+ */
+ public long HPJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+
+ /**
+ * How much HPJ quota to give back to an app based on the number of top app time chunks it
+ * had.
+ */
+ public long HPJ_REWARD_TOP_APP_MS = DEFAULT_HPJ_REWARD_TOP_APP_MS;
+
+ /**
+ * How much HPJ quota to give back to an app based on each non-top user interaction.
+ */
+ public long HPJ_REWARD_INTERACTION_MS = DEFAULT_HPJ_REWARD_INTERACTION_MS;
+
+ /**
+ * How much HPJ quota to give back to an app based on each notification seen event.
+ */
+ public long HPJ_REWARD_NOTIFICATION_SEEN_MS = DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS;
+
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
switch (key) {
@@ -2348,6 +3011,15 @@ public final class QuotaController extends StateController {
updateRateLimitingConstantsLocked();
break;
+ case KEY_HPJ_LIMIT_ACTIVE_MS:
+ case KEY_HPJ_LIMIT_WORKING_MS:
+ case KEY_HPJ_LIMIT_FREQUENT_MS:
+ case KEY_HPJ_LIMIT_RARE_MS:
+ case KEY_HPJ_LIMIT_RESTRICTED_MS:
+ case KEY_HPJ_WINDOW_SIZE_MS:
+ updateHpjLimitConstantsLocked();
+ break;
+
case KEY_MAX_JOB_COUNT_ACTIVE:
MAX_JOB_COUNT_ACTIVE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_ACTIVE);
int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
@@ -2458,6 +3130,64 @@ public final class QuotaController extends StateController {
mInQuotaAlarmListener.setMinQuotaCheckDelayMs(
Math.min(15 * MINUTE_IN_MILLIS, Math.max(0, MIN_QUOTA_CHECK_DELAY_MS)));
break;
+ case KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS:
+ // We don't need to re-evaluate execution stats or constraint status for this.
+ HPJ_TOP_APP_TIME_CHUNK_SIZE_MS =
+ properties.getLong(key, DEFAULT_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS);
+ // Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
+ long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS,
+ Math.max(1, HPJ_TOP_APP_TIME_CHUNK_SIZE_MS));
+ if (mHpjTopAppTimeChunkSizeMs != newChunkSizeMs) {
+ mHpjTopAppTimeChunkSizeMs = newChunkSizeMs;
+ if (mHpjTopAppTimeChunkSizeMs < mHpjRewardTopAppMs) {
+ // Not making chunk sizes and top rewards to be the upper/lower
+ // limits of the other to allow trying different policies. Just log
+ // the discrepancy.
+ Slog.w(TAG, "HPJ top app time chunk less than reward: "
+ + mHpjTopAppTimeChunkSizeMs + " vs " + mHpjRewardTopAppMs);
+ }
+ }
+ break;
+ case KEY_HPJ_REWARD_TOP_APP_MS:
+ // We don't need to re-evaluate execution stats or constraint status for this.
+ HPJ_REWARD_TOP_APP_MS =
+ properties.getLong(key, DEFAULT_HPJ_REWARD_TOP_APP_MS);
+ // Limit top reward to be in the range [10 seconds, 15 minutes] per event.
+ long newTopReward = Math.min(15 * MINUTE_IN_MILLIS,
+ Math.max(10 * SECOND_IN_MILLIS, HPJ_REWARD_TOP_APP_MS));
+ if (mHpjRewardTopAppMs != newTopReward) {
+ mHpjRewardTopAppMs = newTopReward;
+ if (mHpjTopAppTimeChunkSizeMs < mHpjRewardTopAppMs) {
+ // Not making chunk sizes and top rewards to be the upper/lower
+ // limits of the other to allow trying different policies. Just log
+ // the discrepancy.
+ Slog.w(TAG, "HPJ top app time chunk less than reward: "
+ + mHpjTopAppTimeChunkSizeMs + " vs " + mHpjRewardTopAppMs);
+ }
+ }
+ break;
+ case KEY_HPJ_REWARD_INTERACTION_MS:
+ // We don't need to re-evaluate execution stats or constraint status for this.
+ HPJ_REWARD_INTERACTION_MS =
+ properties.getLong(key, DEFAULT_HPJ_REWARD_INTERACTION_MS);
+ // Limit interaction reward to be in the range [5 seconds, 15 minutes] per
+ // event.
+ long newInteractionReward = Math.min(15 * MINUTE_IN_MILLIS,
+ Math.max(5 * SECOND_IN_MILLIS, HPJ_REWARD_INTERACTION_MS));
+ if (mHpjRewardInteractionMs != newInteractionReward) {
+ mHpjRewardInteractionMs = newInteractionReward;
+ }
+ break;
+ case KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS:
+ // We don't need to re-evaluate execution stats or constraint status for this.
+ HPJ_REWARD_NOTIFICATION_SEEN_MS =
+ properties.getLong(key, DEFAULT_HPJ_REWARD_NOTIFICATION_SEEN_MS);
+ // Limit notification seen reward to be in the range [0, 5] minutes per event.
+ long newNotiSeenReward = Math.min(5 * MINUTE_IN_MILLIS,
+ Math.max(0, HPJ_REWARD_NOTIFICATION_SEEN_MS));
+ if (mHpjRewardNotificationSeenMs != newNotiSeenReward) {
+ mHpjRewardNotificationSeenMs = newNotiSeenReward;
+ }
}
}
@@ -2598,6 +3328,75 @@ public final class QuotaController extends StateController {
}
}
+ private void updateHpjLimitConstantsLocked() {
+ if (mHpjLimitConstantsUpdated) {
+ return;
+ }
+ mHpjLimitConstantsUpdated = true;
+
+ // Query the values as an atomic set.
+ final DeviceConfig.Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_HPJ_LIMIT_ACTIVE_MS, KEY_HPJ_LIMIT_WORKING_MS,
+ KEY_HPJ_LIMIT_FREQUENT_MS, KEY_HPJ_LIMIT_RARE_MS,
+ KEY_HPJ_LIMIT_RESTRICTED_MS, KEY_HPJ_WINDOW_SIZE_MS);
+ HPJ_LIMIT_ACTIVE_MS = properties.getLong(
+ KEY_HPJ_LIMIT_ACTIVE_MS, DEFAULT_HPJ_LIMIT_ACTIVE_MS);
+ HPJ_LIMIT_WORKING_MS = properties.getLong(
+ KEY_HPJ_LIMIT_WORKING_MS, DEFAULT_HPJ_LIMIT_WORKING_MS);
+ HPJ_LIMIT_FREQUENT_MS = properties.getLong(
+ KEY_HPJ_LIMIT_FREQUENT_MS, DEFAULT_HPJ_LIMIT_FREQUENT_MS);
+ HPJ_LIMIT_RARE_MS = properties.getLong(
+ KEY_HPJ_LIMIT_RARE_MS, DEFAULT_HPJ_LIMIT_RARE_MS);
+ HPJ_LIMIT_RESTRICTED_MS = properties.getLong(
+ KEY_HPJ_LIMIT_RESTRICTED_MS, DEFAULT_HPJ_LIMIT_RESTRICTED_MS);
+ HPJ_WINDOW_SIZE_MS = properties.getLong(
+ KEY_HPJ_WINDOW_SIZE_MS, DEFAULT_HPJ_WINDOW_SIZE_MS);
+
+ // The window must be in the range [1 hour, 24 hours].
+ long newWindowSizeMs = Math.max(HOUR_IN_MILLIS,
+ Math.min(MAX_PERIOD_MS, HPJ_WINDOW_SIZE_MS));
+ if (mHpjLimitWindowSizeMs != newWindowSizeMs) {
+ mHpjLimitWindowSizeMs = newWindowSizeMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // The limit must be in the range [15 minutes, window size].
+ long newActiveLimitMs = Math.max(15 * MINUTE_IN_MILLIS,
+ Math.min(newWindowSizeMs, HPJ_LIMIT_ACTIVE_MS));
+ if (mHpjLimitsMs[ACTIVE_INDEX] != newActiveLimitMs) {
+ mHpjLimitsMs[ACTIVE_INDEX] = newActiveLimitMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // The limit must be in the range [15 minutes, active limit].
+ long newWorkingLimitMs = Math.max(15 * MINUTE_IN_MILLIS,
+ Math.min(newActiveLimitMs, HPJ_LIMIT_WORKING_MS));
+ if (mHpjLimitsMs[WORKING_INDEX] != newWorkingLimitMs) {
+ mHpjLimitsMs[WORKING_INDEX] = newWorkingLimitMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // The limit must be in the range [10 minutes, working limit].
+ long newFrequentLimitMs = Math.max(10 * MINUTE_IN_MILLIS,
+ Math.min(newWorkingLimitMs, HPJ_LIMIT_FREQUENT_MS));
+ if (mHpjLimitsMs[FREQUENT_INDEX] != newFrequentLimitMs) {
+ mHpjLimitsMs[FREQUENT_INDEX] = newFrequentLimitMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // The limit must be in the range [10 minutes, frequent limit].
+ long newRareLimitMs = Math.max(10 * MINUTE_IN_MILLIS,
+ Math.min(newFrequentLimitMs, HPJ_LIMIT_RARE_MS));
+ if (mHpjLimitsMs[RARE_INDEX] != newRareLimitMs) {
+ mHpjLimitsMs[RARE_INDEX] = newRareLimitMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // The limit must be in the range [0 minutes, rare limit].
+ long newRestrictedLimitMs = Math.max(0,
+ Math.min(newRareLimitMs, HPJ_LIMIT_RESTRICTED_MS));
+ if (mHpjLimitsMs[RESTRICTED_INDEX] != newRestrictedLimitMs) {
+ mHpjLimitsMs[RESTRICTED_INDEX] = newRestrictedLimitMs;
+ mShouldReevaluateConstraints = true;
+ }
+ }
+
private void dump(IndentingPrintWriter pw) {
pw.println();
pw.println("QuotaController:");
@@ -2628,6 +3427,19 @@ public final class QuotaController extends StateController {
pw.print(KEY_TIMING_SESSION_COALESCING_DURATION_MS,
TIMING_SESSION_COALESCING_DURATION_MS).println();
pw.print(KEY_MIN_QUOTA_CHECK_DELAY_MS, MIN_QUOTA_CHECK_DELAY_MS).println();
+
+ pw.print(KEY_HPJ_LIMIT_ACTIVE_MS, HPJ_LIMIT_ACTIVE_MS).println();
+ pw.print(KEY_HPJ_LIMIT_WORKING_MS, HPJ_LIMIT_WORKING_MS).println();
+ pw.print(KEY_HPJ_LIMIT_FREQUENT_MS, HPJ_LIMIT_FREQUENT_MS).println();
+ pw.print(KEY_HPJ_LIMIT_RARE_MS, HPJ_LIMIT_RARE_MS).println();
+ pw.print(KEY_HPJ_LIMIT_RESTRICTED_MS, HPJ_LIMIT_RESTRICTED_MS).println();
+ pw.print(KEY_HPJ_WINDOW_SIZE_MS, HPJ_WINDOW_SIZE_MS).println();
+ pw.print(KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, HPJ_TOP_APP_TIME_CHUNK_SIZE_MS).println();
+ pw.print(KEY_HPJ_REWARD_TOP_APP_MS, HPJ_REWARD_TOP_APP_MS).println();
+ pw.print(KEY_HPJ_REWARD_INTERACTION_MS, HPJ_REWARD_INTERACTION_MS).println();
+ pw.print(KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS,
+ HPJ_REWARD_NOTIFICATION_SEEN_MS).println();
+
pw.decreaseIndent();
}
@@ -2675,6 +3487,24 @@ public final class QuotaController extends StateController {
TIMING_SESSION_COALESCING_DURATION_MS);
proto.write(ConstantsProto.QuotaController.MIN_QUOTA_CHECK_DELAY_MS,
MIN_QUOTA_CHECK_DELAY_MS);
+
+ proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_ACTIVE_MS, HPJ_LIMIT_ACTIVE_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_WORKING_MS, HPJ_LIMIT_WORKING_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_FREQUENT_MS,
+ HPJ_LIMIT_FREQUENT_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_RARE_MS, HPJ_LIMIT_RARE_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_LIMIT_RESTRICTED_MS,
+ HPJ_LIMIT_RESTRICTED_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_WINDOW_SIZE_MS, HPJ_WINDOW_SIZE_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_TOP_APP_TIME_CHUNK_SIZE_MS,
+ HPJ_TOP_APP_TIME_CHUNK_SIZE_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_REWARD_TOP_APP_MS,
+ HPJ_REWARD_TOP_APP_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_REWARD_INTERACTION_MS,
+ HPJ_REWARD_INTERACTION_MS);
+ proto.write(ConstantsProto.QuotaController.HPJ_REWARD_NOTIFICATION_SEEN_MS,
+ HPJ_REWARD_NOTIFICATION_SEEN_MS);
+
proto.end(qcToken);
}
}
@@ -2717,6 +3547,49 @@ public final class QuotaController extends StateController {
}
@VisibleForTesting
+ @NonNull
+ long[] getHpjLimitsMs() {
+ return mHpjLimitsMs;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ long getHpjLimitWindowSizeMs() {
+ return mHpjLimitWindowSizeMs;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ long getHpjRewardInteractionMs() {
+ return mHpjRewardInteractionMs;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ long getHpjRewardNotificationSeenMs() {
+ return mHpjRewardNotificationSeenMs;
+ }
+
+ @VisibleForTesting
+ @NonNull
+ long getHpjRewardTopAppMs() {
+ return mHpjRewardTopAppMs;
+ }
+
+
+ @VisibleForTesting
+ @Nullable
+ List<TimingSession> getHpjTimingSessions(int userId, String packageName) {
+ return mHpjTimingSessions.get(userId, packageName);
+ }
+
+ @VisibleForTesting
+ @NonNull
+ long getHpjTopAppTimeChunkSizeMs() {
+ return mHpjTopAppTimeChunkSizeMs;
+ }
+
+ @VisibleForTesting
long getInQuotaBufferMs() {
return mQuotaBufferMs;
}
@@ -2763,6 +3636,11 @@ public final class QuotaController extends StateController {
return mQcConstants;
}
+ @VisibleForTesting
+ boolean isActiveBackgroundProcessing() {
+ return mHandler.mIsProcessing;
+ }
+
//////////////////////////// DATA DUMP //////////////////////////////
@Override
@@ -2805,16 +3683,24 @@ public final class QuotaController extends StateController {
pw.increaseIndent();
pw.print(JobStatus.bucketName(js.getEffectiveStandbyBucket()));
pw.print(", ");
- if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
- pw.print("within quota");
+ if (js.shouldTreatAsForegroundJob()) {
+ pw.print("within HPJ quota");
+ } else if (js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
+ pw.print("within regular quota");
} else {
pw.print("not within quota");
}
pw.print(", ");
- pw.print(getRemainingExecutionTimeLocked(js));
- pw.print("ms remaining in quota");
- pw.decreaseIndent();
+ if (js.shouldTreatAsForegroundJob()) {
+ pw.print(getRemainingHpjExecutionTimeLocked(
+ js.getSourceUserId(), js.getSourcePackageName()));
+ pw.print("ms remaining in HPJ quota");
+ } else {
+ pw.print(getRemainingExecutionTimeLocked(js));
+ pw.print("ms remaining in quota");
+ }
pw.println();
+ pw.decreaseIndent();
}
});
@@ -2841,6 +3727,33 @@ public final class QuotaController extends StateController {
}
}
+ pw.println();
+ for (int u = 0; u < mHpjPkgTimers.numMaps(); ++u) {
+ final int userId = mHpjPkgTimers.keyAt(u);
+ for (int p = 0; p < mHpjPkgTimers.numElementsForKey(userId); ++p) {
+ final String pkgName = mHpjPkgTimers.keyAt(u, p);
+ mHpjPkgTimers.valueAt(u, p).dump(pw, predicate);
+ pw.println();
+ List<TimingSession> sessions = mHpjTimingSessions.get(userId, pkgName);
+ if (sessions != null) {
+ pw.increaseIndent();
+ pw.println("Saved sessions:");
+ pw.increaseIndent();
+ for (int j = sessions.size() - 1; j >= 0; j--) {
+ TimingSession session = sessions.get(j);
+ session.dump(pw);
+ }
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+ }
+
+ pw.println();
+ mTopAppTrackers.forEach((timer) -> timer.dump(pw));
+
+ pw.println();
pw.println("Cached execution stats:");
pw.increaseIndent();
for (int u = 0; u < mExecutionStatsCache.numMaps(); ++u) {
@@ -2865,6 +3778,22 @@ public final class QuotaController extends StateController {
pw.decreaseIndent();
pw.println();
+ pw.println("HPJ debits:");
+ pw.increaseIndent();
+ for (int u = 0; u < mHpjStats.numMaps(); ++u) {
+ final int userId = mHpjStats.keyAt(u);
+ for (int p = 0; p < mHpjStats.numElementsForKey(userId); ++p) {
+ final String pkgName = mHpjStats.keyAt(u, p);
+ ShrinkableDebits debits = mHpjStats.valueAt(u, p);
+
+ pw.print(string(userId, pkgName));
+ pw.print(": ");
+ debits.dumpLocked(pw);
+ }
+ }
+ pw.decreaseIndent();
+
+ pw.println();
mInQuotaAlarmListener.dumpLocked(pw);
pw.decreaseIndent();
}
@@ -2919,6 +3848,12 @@ public final class QuotaController extends StateController {
js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
proto.write(StateControllerProto.QuotaController.TrackedJob.REMAINING_QUOTA_MS,
getRemainingExecutionTimeLocked(js));
+ proto.write(
+ StateControllerProto.QuotaController.TrackedJob.IS_REQUESTED_FOREGROUND_JOB,
+ js.isRequestedForegroundJob());
+ proto.write(
+ StateControllerProto.QuotaController.TrackedJob.IS_WITHIN_FG_JOB_QUOTA,
+ js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
proto.end(jsToken);
}
});
@@ -2929,8 +3864,15 @@ public final class QuotaController extends StateController {
final String pkgName = mPkgTimers.keyAt(u, p);
final long psToken = proto.start(
StateControllerProto.QuotaController.PACKAGE_STATS);
+
mPkgTimers.valueAt(u, p).dump(proto,
StateControllerProto.QuotaController.PackageStats.TIMER, predicate);
+ final Timer hpjTimer = mHpjPkgTimers.get(userId, pkgName);
+ if (hpjTimer != null) {
+ hpjTimer.dump(proto,
+ StateControllerProto.QuotaController.PackageStats.FG_JOB_TIMER,
+ predicate);
+ }
List<TimingSession> sessions = mTimingSessions.get(userId, pkgName);
if (sessions != null) {
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index bf4323ddfb0b..e4299f52ff7d 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -45,6 +45,7 @@ filegroup {
visibility: [
"//frameworks/base", // For the "global" stubs.
"//frameworks/base/apex/statsd:__subpackages__",
+ "//packages/modules/StatsD/apex:__subpackages__",
],
}
java_sdk_library {
@@ -72,7 +73,10 @@ java_sdk_library {
hostdex: true, // for hiddenapi check
- impl_library_visibility: ["//frameworks/base/apex/statsd/framework/test:__subpackages__"],
+ impl_library_visibility: [
+ "//frameworks/base/apex/statsd/framework/test:__subpackages__",
+ "//packages/modules/StatsD/apex/framework/test:__subpackages__",
+ ],
apex_available: [
"com.android.os.statsd",
diff --git a/cmds/statsd/.clang-format b/cmds/statsd/.clang-format
deleted file mode 100644
index cead3a079435..000000000000
--- a/cmds/statsd/.clang-format
+++ /dev/null
@@ -1,17 +0,0 @@
-BasedOnStyle: Google
-AllowShortIfStatementsOnASingleLine: true
-AllowShortFunctionsOnASingleLine: false
-AllowShortLoopsOnASingleLine: true
-BinPackArguments: true
-BinPackParameters: true
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ContinuationIndentWidth: 8
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-AccessModifierOffset: -4
-IncludeCategories:
- - Regex: '^"Log\.h"'
- Priority: -1
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
deleted file mode 100644
index 6aad82fda915..000000000000
--- a/cmds/statsd/Android.bp
+++ /dev/null
@@ -1,444 +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.
-//
-
-cc_defaults {
- name: "statsd_defaults",
-
- srcs: [
- "src/active_config_list.proto",
- "src/anomaly/AlarmMonitor.cpp",
- "src/anomaly/AlarmTracker.cpp",
- "src/anomaly/AnomalyTracker.cpp",
- "src/anomaly/DurationAnomalyTracker.cpp",
- "src/anomaly/subscriber_util.cpp",
- "src/condition/CombinationConditionTracker.cpp",
- "src/condition/condition_util.cpp",
- "src/condition/ConditionWizard.cpp",
- "src/condition/SimpleConditionTracker.cpp",
- "src/config/ConfigKey.cpp",
- "src/config/ConfigListener.cpp",
- "src/config/ConfigManager.cpp",
- "src/experiment_ids.proto",
- "src/external/Perfetto.cpp",
- "src/external/PullResultReceiver.cpp",
- "src/external/puller_util.cpp",
- "src/external/StatsCallbackPuller.cpp",
- "src/external/StatsPuller.cpp",
- "src/external/StatsPullerManager.cpp",
- "src/external/TrainInfoPuller.cpp",
- "src/FieldValue.cpp",
- "src/guardrail/StatsdStats.cpp",
- "src/hash.cpp",
- "src/HashableDimensionKey.cpp",
- "src/logd/LogEvent.cpp",
- "src/logd/LogEventQueue.cpp",
- "src/matchers/CombinationAtomMatchingTracker.cpp",
- "src/matchers/EventMatcherWizard.cpp",
- "src/matchers/matcher_util.cpp",
- "src/matchers/SimpleAtomMatchingTracker.cpp",
- "src/metadata_util.cpp",
- "src/metrics/CountMetricProducer.cpp",
- "src/metrics/duration_helper/MaxDurationTracker.cpp",
- "src/metrics/duration_helper/OringDurationTracker.cpp",
- "src/metrics/DurationMetricProducer.cpp",
- "src/metrics/EventMetricProducer.cpp",
- "src/metrics/GaugeMetricProducer.cpp",
- "src/metrics/MetricProducer.cpp",
- "src/metrics/MetricsManager.cpp",
- "src/metrics/parsing_utils/config_update_utils.cpp",
- "src/metrics/parsing_utils/metrics_manager_util.cpp",
- "src/metrics/ValueMetricProducer.cpp",
- "src/packages/UidMap.cpp",
- "src/shell/shell_config.proto",
- "src/shell/ShellSubscriber.cpp",
- "src/socket/StatsSocketListener.cpp",
- "src/state/StateManager.cpp",
- "src/state/StateTracker.cpp",
- "src/stats_log_util.cpp",
- "src/statscompanion_util.cpp",
- "src/statsd_config.proto",
- "src/statsd_metadata.proto",
- "src/StatsLogProcessor.cpp",
- "src/StatsService.cpp",
- "src/storage/StorageManager.cpp",
- "src/subscriber/IncidentdReporter.cpp",
- "src/subscriber/SubscriberReporter.cpp",
- "src/uid_data.proto",
- "src/utils/MultiConditionTrigger.cpp",
- ],
-
- local_include_dirs: [
- "src",
- ],
-
- static_libs: [
- "libbase",
- "libcutils",
- "libgtest_prod",
- "libprotoutil",
- "libstatslog_statsd",
- "libsysutils",
- "libutils",
- "statsd-aidl-ndk_platform",
- ],
- shared_libs: [
- "libbinder_ndk",
- "libincident",
- "liblog",
- ],
-}
-
-genrule {
- name: "statslog_statsd.h",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util",
- out: [
- "statslog_statsd.h",
- ],
-}
-
-genrule {
- name: "statslog_statsd.cpp",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h",
- out: [
- "statslog_statsd.cpp",
- ],
-}
-
-genrule {
- name: "statslog_statsdtest.h",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsdtest.h --module statsdtest --namespace android,os,statsd,util",
- out: [
- "statslog_statsdtest.h",
- ],
-}
-
-genrule {
- name: "statslog_statsdtest.cpp",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsdtest.cpp --module statsdtest --namespace android,os,statsd,util --importHeader statslog_statsdtest.h",
- out: [
- "statslog_statsdtest.cpp",
- ],
-}
-
-cc_library_static {
- name: "libstatslog_statsdtest",
- generated_sources: ["statslog_statsdtest.cpp"],
- generated_headers: ["statslog_statsdtest.h"],
- export_generated_headers: ["statslog_statsdtest.h"],
- shared_libs: [
- "libstatssocket",
- "libstatspull",
- ],
-}
-
-cc_library_static {
- name: "libstatslog_statsd",
- generated_sources: ["statslog_statsd.cpp"],
- generated_headers: ["statslog_statsd.h"],
- export_generated_headers: ["statslog_statsd.h"],
- apex_available: [
- "com.android.os.statsd",
- "test_com.android.os.statsd",
- ],
- shared_libs: [
- "libstatssocket",
- "libstatspull",
- ],
- export_shared_lib_headers: [
- "libstatspull",
- ],
-}
-
-// =========
-// statsd
-// =========
-
-cc_binary {
- name: "statsd",
- defaults: ["statsd_defaults"],
-
- srcs: ["src/main.cpp"],
-
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- "-Wno-unused-parameter",
- // optimize for size (protobuf glop can get big)
- "-Os",
- // "-g",
- // "-O0",
- ],
-
- product_variables: {
- eng: {
- // Enable sanitizer ONLY on eng builds
- //sanitize: {
- // address: true,
- //},
- },
- },
-
- proto: {
- type: "lite",
- static: true,
- },
- stl: "libc++_static",
-
- shared_libs: [
- "libstatssocket",
- ],
-
- apex_available: [
- "com.android.os.statsd",
- "test_com.android.os.statsd",
- ],
-}
-
-// ==============
-// statsd_test
-// ==============
-
-cc_test {
- name: "statsd_test",
- defaults: ["statsd_defaults"],
- test_suites: ["device-tests", "mts"],
- test_config: "statsd_test.xml",
-
- //TODO(b/153588990): Remove when the build system properly separates
- //32bit and 64bit architectures.
- compile_multilib: "both",
- multilib: {
- lib64: {
- suffix: "64",
- },
- lib32: {
- suffix: "32",
- },
- },
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-missing-field-initializers",
- "-Wno-unused-variable",
- "-Wno-unused-function",
- "-Wno-unused-parameter",
- ],
-
- require_root: true,
-
- srcs: [
- // atom_field_options.proto needs field_options.proto, but that is
- // not included in libprotobuf-cpp-lite, so compile it here.
- ":libprotobuf-internal-protos",
- ":libstats_internal_protos",
-
- "src/shell/shell_data.proto",
- "src/stats_log.proto",
- "tests/AlarmMonitor_test.cpp",
- "tests/anomaly/AlarmTracker_test.cpp",
- "tests/anomaly/AnomalyTracker_test.cpp",
- "tests/condition/CombinationConditionTracker_test.cpp",
- "tests/condition/ConditionTimer_test.cpp",
- "tests/condition/SimpleConditionTracker_test.cpp",
- "tests/ConfigManager_test.cpp",
- "tests/e2e/Alarm_e2e_test.cpp",
- "tests/e2e/Anomaly_count_e2e_test.cpp",
- "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
- "tests/e2e/Attribution_e2e_test.cpp",
- "tests/e2e/ConfigTtl_e2e_test.cpp",
- "tests/e2e/ConfigUpdate_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",
- "tests/e2e/MetricActivation_e2e_test.cpp",
- "tests/e2e/MetricConditionLink_e2e_test.cpp",
- "tests/e2e/PartialBucket_e2e_test.cpp",
- "tests/e2e/ValueMetric_pull_e2e_test.cpp",
- "tests/e2e/WakelockDuration_e2e_test.cpp",
- "tests/external/puller_util_test.cpp",
- "tests/external/StatsCallbackPuller_test.cpp",
- "tests/external/StatsPuller_test.cpp",
- "tests/external/StatsPullerManager_test.cpp",
- "tests/FieldValue_test.cpp",
- "tests/guardrail/StatsdStats_test.cpp",
- "tests/HashableDimensionKey_test.cpp",
- "tests/indexed_priority_queue_test.cpp",
- "tests/log_event/LogEventQueue_test.cpp",
- "tests/LogEntryMatcher_test.cpp",
- "tests/LogEvent_test.cpp",
- "tests/metadata_util_test.cpp",
- "tests/metrics/CountMetricProducer_test.cpp",
- "tests/metrics/DurationMetricProducer_test.cpp",
- "tests/metrics/EventMetricProducer_test.cpp",
- "tests/metrics/GaugeMetricProducer_test.cpp",
- "tests/metrics/MaxDurationTracker_test.cpp",
- "tests/metrics/metrics_test_helper.cpp",
- "tests/metrics/OringDurationTracker_test.cpp",
- "tests/metrics/ValueMetricProducer_test.cpp",
- "tests/metrics/parsing_utils/config_update_utils_test.cpp",
- "tests/metrics/parsing_utils/metrics_manager_util_test.cpp",
- "tests/MetricsManager_test.cpp",
- "tests/shell/ShellSubscriber_test.cpp",
- "tests/state/StateTracker_test.cpp",
- "tests/statsd_test_util.cpp",
- "tests/StatsLogProcessor_test.cpp",
- "tests/StatsService_test.cpp",
- "tests/storage/StorageManager_test.cpp",
- "tests/UidMap_test.cpp",
- "tests/utils/MultiConditionTrigger_test.cpp",
- ],
-
- static_libs: [
- "libgmock",
- "libplatformprotos",
- "libstatslog_statsdtest",
- "libstatssocket_private",
- ],
-
- proto: {
- type: "lite",
- include_dirs: [
- "external/protobuf/src",
- "frameworks/proto_logging/stats",
- ],
- },
-
-}
-
-//#############################
-// statsd micro benchmark
-//#############################
-
-cc_benchmark {
- name: "statsd_benchmark",
- defaults: ["statsd_defaults"],
-
- srcs: [
- // atom_field_options.proto needs field_options.proto, but that is
- // not included in libprotobuf-cpp-lite, so compile it here.
- ":libprotobuf-internal-protos",
- ":libstats_internal_protos",
-
- "benchmark/duration_metric_benchmark.cpp",
- "benchmark/filter_value_benchmark.cpp",
- "benchmark/get_dimensions_for_condition_benchmark.cpp",
- "benchmark/hello_world_benchmark.cpp",
- "benchmark/log_event_benchmark.cpp",
- "benchmark/main.cpp",
- "benchmark/metric_util.cpp",
- "benchmark/stats_write_benchmark.cpp",
- "src/stats_log.proto",
- ],
-
- proto: {
- type: "lite",
- include_dirs: [
- "external/protobuf/src",
- "frameworks/proto_logging/stats",
- ],
- },
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- "-Wno-unused-variable",
- "-Wno-unused-function",
-
- // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
- "-Wno-varargs",
- ],
-
- static_libs: [
- "libplatformprotos",
- "libstatssocket_private",
- ],
-
- shared_libs: [
- "libgtest_prod",
- "libprotobuf-cpp-lite",
- "libstatslog",
- ],
-}
-
-// ==== java proto device library (for test only) ==============================
-java_library {
- name: "statsdprotolite",
- sdk_version: "core_current",
- proto: {
- type: "lite",
- include_dirs: [
- "external/protobuf/src",
- "frameworks/proto_logging/stats",
- ],
- },
-
- srcs: [
- ":libstats_atoms_proto",
- "src/shell/shell_config.proto",
- "src/shell/shell_data.proto",
- "src/stats_log.proto",
- "src/statsd_config.proto",
- ],
-
- static_libs: [
- "platformprotoslite",
- ],
- // Protos have lots of MissingOverride and similar.
- errorprone: {
- javacflags: ["-XepDisableAllChecks"],
- },
-}
-
-java_library {
- name: "statsdprotonano",
- sdk_version: "9",
- proto: {
- type: "nano",
- output_params: ["store_unknown_fields=true"],
- include_dirs: [
- "external/protobuf/src",
- "frameworks/proto_logging/stats",
- ],
- },
- srcs: [
- ":libstats_atoms_proto",
- "src/shell/shell_config.proto",
- "src/shell/shell_data.proto",
- "src/stats_log.proto",
- "src/statsd_config.proto",
- ],
- static_libs: [
- "platformprotosnano",
- ],
- // Protos have lots of MissingOverride and similar.
- errorprone: {
- javacflags: ["-XepDisableAllChecks"],
- },
-}
-
-// Filegroup for statsd config proto definition.
-filegroup {
- name: "statsd-config-proto-def",
- srcs: ["src/statsd_config.proto"],
-}
diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS
deleted file mode 100644
index 4e4e11988b62..000000000000
--- a/cmds/statsd/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-baligh@google.com
diff --git a/cmds/statsd/TEST_MAPPING b/cmds/statsd/TEST_MAPPING
deleted file mode 100644
index a7a4cf14182e..000000000000
--- a/cmds/statsd/TEST_MAPPING
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "presubmit" : [
- {
- "name" : "statsd_test"
- }
- ],
-
- "postsubmit" : [
- {
- "name" : "CtsStatsdHostTestCases"
- },
- {
- "name" : "GtsStatsdHostTestCases"
- }
- ]
-
-}
diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
deleted file mode 100644
index 2d315d9395bc..000000000000
--- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <vector>
-#include "benchmark/benchmark.h"
-#include "FieldValue.h"
-#include "HashableDimensionKey.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-#include "metric_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-static StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
- DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
- StatsdConfig config;
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
- dimensions->add_child()->set_field(2); // job name field.
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
- {Position::FIRST});
- if (addExtraDimensionInCondition) {
- syncDimension->add_child()->set_field(2 /* name field*/);
- }
-
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(StringToId("CombinationPredicate"));
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(combinationPredicate->id());
- metric->set_aggregation_type(aggregationType);
- auto dimensionWhat = metric->mutable_dimensions_in_what();
- dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(2); // job name field.
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-static StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition(
- DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
- StatsdConfig config;
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- *dimensions = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- dimensions->add_child()->set_field(2); // job name field.
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- if (addExtraDimensionInCondition) {
- syncDimension->add_child()->set_field(2 /* name field*/);
- }
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(StringToId("CombinationPredicate"));
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(combinationPredicate->id());
- metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-
- auto links = metric->add_links();
- links->set_condition(isSyncingPredicate.id());
- *links->mutable_fields_in_what() =
- CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- *links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-static void BM_DurationMetricNoLink(benchmark::State& state) {
- ConfigKey cfgKey;
- auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
- DurationMetric::SUM, false);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11,
- android::view::DISPLAY_STATE_OFF));
- events.push_back(
- CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON));
-
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102,
- android::view::DISPLAY_STATE_OFF));
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
- android::view::DISPLAY_STATE_ON));
-
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650,
- android::view::DISPLAY_STATE_OFF));
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
- android::view::DISPLAY_STATE_ON));
-
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640,
- android::view::DISPLAY_STATE_OFF));
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650,
- android::view::DISPLAY_STATE_ON));
-
- vector<int> attributionUids1 = {9999};
- vector<string> attributionTags1 = {""};
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "job0"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
- attributionTags1, "job0"));
-
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1,
- attributionTags1, "job2"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1,
- attributionTags1, "job2"));
-
- vector<int> attributionUids2 = {8888};
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
- attributionTags1, "job2"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
- attributionUids2, attributionTags1, "job2"));
-
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600,
- attributionUids2, attributionTags1, "job1"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
- attributionUids2, attributionTags1, "job1"));
-
- vector<int> attributionUids3 = {111, 222, 222};
- vector<string> attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3,
- attributionTags3, "ReadEmail"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3,
- "ReadEmail"));
-
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3,
- attributionTags3, "ReadEmail"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3,
- attributionTags3, "ReadEmail"));
-
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3,
- attributionTags3, "ReadDoc"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3,
- attributionTags3, "ReadDoc"));
-
- vector<int> attributionUids4 = {333, 222, 555};
- vector<string> attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4,
- attributionTags4, "ReadEmail"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4,
- attributionTags4, "ReadEmail"));
- sortLogEventsByTimestamp(&events);
-
- while (state.KeepRunning()) {
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- }
-}
-
-BENCHMARK(BM_DurationMetricNoLink);
-
-
-static void BM_DurationMetricLink(benchmark::State& state) {
- ConfigKey cfgKey;
- auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition(
- DurationMetric::SUM, false);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55,
- android::view::DISPLAY_STATE_OFF));
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120,
- android::view::DISPLAY_STATE_ON));
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121,
- android::view::DISPLAY_STATE_OFF));
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
- android::view::DISPLAY_STATE_ON));
-
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501,
- android::view::DISPLAY_STATE_OFF));
- events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
- android::view::DISPLAY_STATE_ON));
-
- vector<int> attributionUids1 = {111};
- vector<string> attributionTags1 = {"App1"};
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1,
- attributionTags1, "job1"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
- attributionTags1, "job1"));
-
- vector<int> attributionUids2 = {333};
- vector<string> attributionTags2 = {"App2"};
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2,
- attributionTags2, "job2"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2,
- attributionTags2, "job2"));
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
- attributionTags2, "job2"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
- attributionUids2, attributionTags2, "job2"));
-
- vector<int> attributionUids3 = {444};
- vector<string> attributionTags3 = {"App3"};
- events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2,
- attributionUids3, attributionTags3, "job3"));
- events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
- attributionUids3, attributionTags3, "job3"));
-
- vector<int> attributionUids4 = {111, 222, 222};
- vector<string> attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4,
- attributionTags4, "ReadEmail"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4,
- "ReadEmail"));
-
- vector<int> attributionUids5 = {333, 222, 555};
- vector<string> attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5,
- attributionTags5, "ReadEmail"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5,
- attributionTags5, "ReadEmail"));
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5,
- attributionTags5, "ReadDoc"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5,
- attributionTags5, "ReadDoc"));
-
- vector<int> attributionUids6 = {444, 222, 555};
- vector<string> attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"};
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6,
- attributionTags6, "ReadDoc"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6,
- "ReadDoc"));
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6,
- attributionTags6, "ReadDoc"));
- events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6,
- attributionTags6, "ReadDoc"));
- sortLogEventsByTimestamp(&events);
-
- while (state.KeepRunning()) {
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- }
-}
-
-BENCHMARK(BM_DurationMetricLink);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
deleted file mode 100644
index 743ccc4ed451..000000000000
--- a/cmds/statsd/benchmark/filter_value_benchmark.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <vector>
-
-#include "FieldValue.h"
-#include "HashableDimensionKey.h"
-#include "benchmark/benchmark.h"
-#include "logd/LogEvent.h"
-#include "metric_util.h"
-#include "stats_event.h"
-#include "stats_log_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, 1);
- AStatsEvent_overwriteTimestamp(statsEvent, 100000);
-
- std::vector<int> attributionUids = {100, 100};
- std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
- writeAttribution(statsEvent, attributionUids, attributionTags);
-
- AStatsEvent_writeFloat(statsEvent, 3.2f);
- AStatsEvent_writeString(statsEvent, "LOCATION");
- AStatsEvent_writeInt64(statsEvent, 990);
-
- parseStatsEventToLogEvent(statsEvent, event);
-
- field_matcher->set_field(1);
- auto child = field_matcher->add_child();
- child->set_field(1);
- child->set_position(FIRST);
- child->add_child()->set_field(1);
-}
-
-static void BM_FilterValue(benchmark::State& state) {
- LogEvent event(/*uid=*/0, /*pid=*/0);
- FieldMatcher field_matcher;
- createLogEventAndMatcher(&event, &field_matcher);
-
- std::vector<Matcher> matchers;
- translateFieldMatcher(field_matcher, &matchers);
-
- while (state.KeepRunning()) {
- HashableDimensionKey output;
- filterValues(matchers, event.getValues(), &output);
- }
-}
-
-BENCHMARK(BM_FilterValue);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
deleted file mode 100644
index 7a455650a31b..000000000000
--- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <vector>
-
-#include "FieldValue.h"
-#include "HashableDimensionKey.h"
-#include "benchmark/benchmark.h"
-#include "logd/LogEvent.h"
-#include "metric_util.h"
-#include "stats_event.h"
-#include "stats_log_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, 1);
- AStatsEvent_overwriteTimestamp(statsEvent, 100000);
-
- std::vector<int> attributionUids = {100, 100};
- std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
- writeAttribution(statsEvent, attributionUids, attributionTags);
-
- AStatsEvent_writeFloat(statsEvent, 3.2f);
- AStatsEvent_writeString(statsEvent, "LOCATION");
- AStatsEvent_writeInt64(statsEvent, 990);
-
- parseStatsEventToLogEvent(statsEvent, event);
-
- link->conditionId = 1;
-
- FieldMatcher field_matcher;
- field_matcher.set_field(event->GetTagId());
- auto child = field_matcher.add_child();
- child->set_field(1);
- child->set_position(FIRST);
- child->add_child()->set_field(1);
-
- translateFieldMatcher(field_matcher, &link->metricFields);
- field_matcher.set_field(event->GetTagId() + 1);
- translateFieldMatcher(field_matcher, &link->conditionFields);
-}
-
-static void BM_GetDimensionInCondition(benchmark::State& state) {
- Metric2Condition link;
- LogEvent event(/*uid=*/0, /*pid=*/0);
- createLogEventAndLink(&event, &link);
-
- while (state.KeepRunning()) {
- HashableDimensionKey output;
- getDimensionForCondition(event.getValues(), link, &output);
- }
-}
-
-BENCHMARK(BM_GetDimensionInCondition);
-
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/benchmark/hello_world_benchmark.cpp b/cmds/statsd/benchmark/hello_world_benchmark.cpp
deleted file mode 100644
index c732d394bf64..000000000000
--- a/cmds/statsd/benchmark/hello_world_benchmark.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "benchmark/benchmark.h"
-
-static void BM_StringCreation(benchmark::State& state) {
- while (state.KeepRunning()) std::string empty_string;
-}
-// Register the function as a benchmark
-BENCHMARK(BM_StringCreation);
-
-// Define another benchmark
-static void BM_StringCopy(benchmark::State& state) {
- std::string x = "hello";
- while (state.KeepRunning()) std::string copy(x);
-}
-BENCHMARK(BM_StringCopy);
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
deleted file mode 100644
index 057e00bde202..000000000000
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <vector>
-#include "benchmark/benchmark.h"
-#include "logd/LogEvent.h"
-#include "stats_event.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static size_t createAndParseStatsEvent(uint8_t* msg) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
- AStatsEvent_writeInt32(event, 2);
- AStatsEvent_writeFloat(event, 2.0);
- AStatsEvent_build(event);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- memcpy(msg, buf, size);
- return size;
-}
-
-static void BM_LogEventCreation(benchmark::State& state) {
- uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD];
- size_t size = createAndParseStatsEvent(msg);
- while (state.KeepRunning()) {
- LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001);
- benchmark::DoNotOptimize(event.parseBuffer(msg, size));
- }
-}
-BENCHMARK(BM_LogEventCreation);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
deleted file mode 100644
index 89fd3d9b29ab..000000000000
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ /dev/null
@@ -1,379 +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.
-
-#include "metric_util.h"
-
-#include "stats_event.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(atomId);
- return atom_matcher;
-}
-
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
- ScheduledJobStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(3); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateStartScheduledJobAtomMatcher() {
- return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart",
- ScheduledJobStateChanged::STARTED);
-}
-
-AtomMatcher CreateFinishScheduledJobAtomMatcher() {
- return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish",
- ScheduledJobStateChanged::FINISHED);
-}
-
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
- return atom_matcher;
-}
-
-AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId("UidProcessStateChanged"));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
- return atom_matcher;
-}
-
-AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
- WakelockStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(4); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateAcquireWakelockAtomMatcher() {
- return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
-}
-
-AtomMatcher CreateReleaseWakelockAtomMatcher() {
- return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
-}
-
-AtomMatcher CreateScreenStateChangedAtomMatcher(
- const string& name, android::view::DisplayStateEnum state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(1); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateScreenTurnedOnAtomMatcher() {
- return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-}
-
-AtomMatcher CreateScreenTurnedOffAtomMatcher() {
- return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff",
- ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
-}
-
-AtomMatcher CreateSyncStateChangedAtomMatcher(
- const string& name, SyncStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(3); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateSyncStartAtomMatcher() {
- return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
-}
-
-AtomMatcher CreateSyncEndAtomMatcher() {
- return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
-}
-
-AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
- const string& name, ActivityForegroundStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(4); // Activity field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateMoveToBackgroundAtomMatcher() {
- return CreateActivityForegroundStateChangedAtomMatcher(
- "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND);
-}
-
-AtomMatcher CreateMoveToForegroundAtomMatcher() {
- return CreateActivityForegroundStateChangedAtomMatcher(
- "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND);
-}
-
-Predicate CreateScheduledJobPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("ScheduledJobRunningPredicate"));
- predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish"));
- return predicate;
-}
-
-Predicate CreateBatterySaverModePredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("BatterySaverIsOn"));
- predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
- return predicate;
-}
-
-Predicate CreateScreenIsOnPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("ScreenIsOn"));
- predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff"));
- return predicate;
-}
-
-Predicate CreateScreenIsOffPredicate() {
- Predicate predicate;
- predicate.set_id(1111123);
- predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
- return predicate;
-}
-
-Predicate CreateHoldingWakelockPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("HoldingWakelock"));
- predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock"));
- return predicate;
-}
-
-Predicate CreateIsSyncingPredicate() {
- Predicate predicate;
- predicate.set_id(33333333333333);
- predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
- return predicate;
-}
-
-Predicate CreateIsInBackgroundPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("IsInBackground"));
- predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground"));
- return predicate;
-}
-
-void addPredicateToPredicateCombination(const Predicate& predicate,
- Predicate* combinationPredicate) {
- combinationPredicate->mutable_combination()->add_predicate(predicate.id());
-}
-
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
- const std::vector<Position>& positions) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const auto position : positions) {
- auto child = dimensions.add_child();
- child->set_field(1);
- child->set_position(position);
- child->add_child()->set_field(1);
- }
- return dimensions;
-}
-
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
- const std::vector<Position>& positions) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const auto position : positions) {
- auto child = dimensions.add_child();
- child->set_field(1);
- child->set_position(position);
- child->add_child()->set_field(1);
- child->add_child()->set_field(2);
- }
- return dimensions;
-}
-
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const int field : fields) {
- dimensions.add_child()->set_field(field);
- }
- return dimensions;
-}
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
- const vector<string>& attributionTags) {
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
-}
-
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
-}
-
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
- uint64_t timestampNs, const android::view::DisplayStateEnum state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
- const vector<int>& attributionUids, const vector<string>& attributionTags,
- const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, jobName.c_str());
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName) {
- return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
- ScheduledJobStateChanged::STARTED, timestampNs);
-}
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName) {
- return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
- ScheduledJobStateChanged::FINISHED, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name,
- const SyncStateChanged::State state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name) {
- return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
- SyncStateChanged::ON);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name) {
- return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
- SyncStateChanged::OFF);
-}
-
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
- const ConfigKey& key) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- sp<StatsLogProcessor> processor =
- new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; },
- [](const int&, const vector<int64_t>&) { return true; });
- processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
- return processor;
-}
-
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
- std::sort(events->begin(), events->end(),
- [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
- return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs();
- });
-}
-
-int64_t StringToId(const string& str) {
- return static_cast<int64_t>(std::hash<std::string>()(str));
-}
-
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h
deleted file mode 100644
index 3efaa850a921..000000000000
--- a/cmds/statsd/benchmark/metric_util.h
+++ /dev/null
@@ -1,140 +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.
-
-#pragma once
-
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "src/StatsLogProcessor.h"
-#include "src/logd/LogEvent.h"
-#include "stats_event.h"
-#include "statslog.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Create AtomMatcher proto to simply match a specific atom type.
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
-
-// Create AtomMatcher proto for scheduled job state changed.
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
-
-// Create AtomMatcher proto for starting a scheduled job.
-AtomMatcher CreateStartScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for a scheduled job is done.
-AtomMatcher CreateFinishScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for screen brightness state changed.
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
-
-// Create AtomMatcher proto for acquiring wakelock.
-AtomMatcher CreateAcquireWakelockAtomMatcher();
-
-// Create AtomMatcher proto for releasing wakelock.
-AtomMatcher CreateReleaseWakelockAtomMatcher() ;
-
-// Create AtomMatcher proto for screen turned on.
-AtomMatcher CreateScreenTurnedOnAtomMatcher();
-
-// Create AtomMatcher proto for screen turned off.
-AtomMatcher CreateScreenTurnedOffAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned on.
-AtomMatcher CreateSyncStartAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned off.
-AtomMatcher CreateSyncEndAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to background.
-AtomMatcher CreateMoveToBackgroundAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to foreground.
-AtomMatcher CreateMoveToForegroundAtomMatcher();
-
-// Create Predicate proto for screen is off.
-Predicate CreateScreenIsOffPredicate();
-
-// Create Predicate proto for a running scheduled job.
-Predicate CreateScheduledJobPredicate();
-
-// Create Predicate proto for holding wakelock.
-Predicate CreateHoldingWakelockPredicate();
-
-// Create a Predicate proto for app syncing.
-Predicate CreateIsSyncingPredicate();
-
-// Create a Predicate proto for app is in background.
-Predicate CreateIsInBackgroundPredicate();
-
-// Add a predicate to the predicate combination.
-void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
-
-// Create dimensions from primitive fields.
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields);
-
-// Create dimensions by attribution uid and tag.
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
- const std::vector<Position>& positions);
-
-// Create dimensions by attribution uid only.
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
- const std::vector<Position>& positions);
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
- const vector<string>& attributionTags);
-
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
-
-// Create log event for screen state changed.
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
- uint64_t timestampNs, const android::view::DisplayStateEnum state);
-
-// Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName);
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName);
-
-// Create log event when the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name);
-
-// Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
- const ConfigKey& key);
-
-// Util function to sort the log events by timestamp.
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
-
-int64_t StringToId(const string& str);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/benchmark/stats_write_benchmark.cpp b/cmds/statsd/benchmark/stats_write_benchmark.cpp
deleted file mode 100644
index f5a0cd5dfb39..000000000000
--- a/cmds/statsd/benchmark/stats_write_benchmark.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "benchmark/benchmark.h"
-#include <statslog.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static void BM_StatsWrite(benchmark::State& state) {
- const char* reason = "test";
- int64_t boot_end_time = 1234567;
- int64_t total_duration = 100;
- int64_t bootloader_duration = 10;
- int64_t time_since_last_boot = 99999999;
- while (state.KeepRunning()) {
- android::util::stats_write(
- android::util::BOOT_SEQUENCE_REPORTED, reason, reason,
- boot_end_time, total_duration, bootloader_duration, time_since_last_boot);
- total_duration++;
- }
-}
-BENCHMARK(BM_StatsWrite);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
deleted file mode 100644
index c9ccfb93c86d..000000000000
--- a/cmds/statsd/src/FieldValue.cpp
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false
-#include "Log.h"
-#include "FieldValue.h"
-#include "HashableDimensionKey.h"
-#include "math.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth) {
- int32_t field = 0;
- for (int32_t i = 0; i <= depth; i++) {
- int32_t shiftBits = 8 * (kMaxLogDepth - i);
- field |= (pos[i] << shiftBits);
- }
-
- if (includeDepth) {
- field |= (depth << 24);
- }
- return field;
-}
-
-int32_t encodeMatcherMask(int32_t mask[], int32_t depth) {
- return getEncodedField(mask, depth, false) | 0xff000000;
-}
-
-bool Field::matches(const Matcher& matcher) const {
- if (mTag != matcher.mMatcher.getTag()) {
- return false;
- }
- if ((mField & matcher.mMask) == matcher.mMatcher.getField()) {
- return true;
- }
-
- if (matcher.hasAllPositionMatcher() &&
- (mField & (matcher.mMask & kClearAllPositionMatcherMask)) == matcher.mMatcher.getField()) {
- return true;
- }
-
- return false;
-}
-
-void translateFieldMatcher(int tag, const FieldMatcher& matcher, int depth, int* pos, int* mask,
- std::vector<Matcher>* output) {
- if (depth > kMaxLogDepth) {
- ALOGE("depth > 2");
- return;
- }
-
- pos[depth] = matcher.field();
- mask[depth] = 0x7f;
-
- if (matcher.has_position()) {
- depth++;
- if (depth > 2) {
- return;
- }
- switch (matcher.position()) {
- case Position::ALL:
- pos[depth] = 0x00;
- mask[depth] = 0x7f;
- break;
- case Position::ANY:
- pos[depth] = 0;
- mask[depth] = 0;
- break;
- case Position::FIRST:
- pos[depth] = 1;
- mask[depth] = 0x7f;
- break;
- case Position::LAST:
- pos[depth] = 0x80;
- mask[depth] = 0x80;
- break;
- case Position::POSITION_UNKNOWN:
- pos[depth] = 0;
- mask[depth] = 0;
- break;
- }
- }
-
- if (matcher.child_size() == 0) {
- output->push_back(Matcher(Field(tag, pos, depth), encodeMatcherMask(mask, depth)));
- } else {
- for (const auto& child : matcher.child()) {
- translateFieldMatcher(tag, child, depth + 1, pos, mask, output);
- }
- }
-}
-
-void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output) {
- int pos[] = {1, 1, 1};
- int mask[] = {0x7f, 0x7f, 0x7f};
- int tag = matcher.field();
- for (const auto& child : matcher.child()) {
- translateFieldMatcher(tag, child, 0, pos, mask, output);
- }
-}
-
-bool isAttributionUidField(const FieldValue& value) {
- return isAttributionUidField(value.mField, value.mValue);
-}
-
-int32_t getUidIfExists(const FieldValue& value) {
- // the field is uid field if the field is the uid field in attribution node
- // or annotated as such in the atom
- bool isUid = isAttributionUidField(value) || isUidField(value);
- return isUid ? value.mValue.int_value : -1;
-}
-
-bool isAttributionUidField(const Field& field, const Value& value) {
- int f = field.getField() & 0xff007f;
- if (f == 0x10001 && value.getType() == INT) {
- return true;
- }
- return false;
-}
-
-bool isUidField(const FieldValue& fieldValue) {
- return fieldValue.mAnnotations.isUidField();
-}
-
-Value::Value(const Value& from) {
- type = from.getType();
- switch (type) {
- case INT:
- int_value = from.int_value;
- break;
- case LONG:
- long_value = from.long_value;
- break;
- case FLOAT:
- float_value = from.float_value;
- break;
- case DOUBLE:
- double_value = from.double_value;
- break;
- case STRING:
- str_value = from.str_value;
- break;
- case STORAGE:
- storage_value = from.storage_value;
- break;
- default:
- break;
- }
-}
-
-std::string Value::toString() const {
- switch (type) {
- case INT:
- return std::to_string(int_value) + "[I]";
- case LONG:
- return std::to_string(long_value) + "[L]";
- case FLOAT:
- return std::to_string(float_value) + "[F]";
- case DOUBLE:
- return std::to_string(double_value) + "[D]";
- case STRING:
- return str_value + "[S]";
- case STORAGE:
- return "bytes of size " + std::to_string(storage_value.size()) + "[ST]";
- default:
- return "[UNKNOWN]";
- }
-}
-
-bool Value::isZero() const {
- switch (type) {
- case INT:
- return int_value == 0;
- case LONG:
- return long_value == 0;
- case FLOAT:
- return fabs(float_value) <= std::numeric_limits<float>::epsilon();
- case DOUBLE:
- return fabs(double_value) <= std::numeric_limits<double>::epsilon();
- case STRING:
- return str_value.size() == 0;
- case STORAGE:
- return storage_value.size() == 0;
- default:
- return false;
- }
-}
-
-bool Value::operator==(const Value& that) const {
- if (type != that.getType()) return false;
-
- switch (type) {
- case INT:
- return int_value == that.int_value;
- case LONG:
- return long_value == that.long_value;
- case FLOAT:
- return float_value == that.float_value;
- case DOUBLE:
- return double_value == that.double_value;
- case STRING:
- return str_value == that.str_value;
- case STORAGE:
- return storage_value == that.storage_value;
- default:
- return false;
- }
-}
-
-bool Value::operator!=(const Value& that) const {
- if (type != that.getType()) return true;
- switch (type) {
- case INT:
- return int_value != that.int_value;
- case LONG:
- return long_value != that.long_value;
- case FLOAT:
- return float_value != that.float_value;
- case DOUBLE:
- return double_value != that.double_value;
- case STRING:
- return str_value != that.str_value;
- case STORAGE:
- return storage_value != that.storage_value;
- default:
- return false;
- }
-}
-
-bool Value::operator<(const Value& that) const {
- if (type != that.getType()) return type < that.getType();
-
- switch (type) {
- case INT:
- return int_value < that.int_value;
- case LONG:
- return long_value < that.long_value;
- case FLOAT:
- return float_value < that.float_value;
- case DOUBLE:
- return double_value < that.double_value;
- case STRING:
- return str_value < that.str_value;
- case STORAGE:
- return storage_value < that.storage_value;
- default:
- return false;
- }
-}
-
-bool Value::operator>(const Value& that) const {
- if (type != that.getType()) return type > that.getType();
-
- switch (type) {
- case INT:
- return int_value > that.int_value;
- case LONG:
- return long_value > that.long_value;
- case FLOAT:
- return float_value > that.float_value;
- case DOUBLE:
- return double_value > that.double_value;
- case STRING:
- return str_value > that.str_value;
- case STORAGE:
- return storage_value > that.storage_value;
- default:
- return false;
- }
-}
-
-bool Value::operator>=(const Value& that) const {
- if (type != that.getType()) return type >= that.getType();
-
- switch (type) {
- case INT:
- return int_value >= that.int_value;
- case LONG:
- return long_value >= that.long_value;
- case FLOAT:
- return float_value >= that.float_value;
- case DOUBLE:
- return double_value >= that.double_value;
- case STRING:
- return str_value >= that.str_value;
- case STORAGE:
- return storage_value >= that.storage_value;
- default:
- return false;
- }
-}
-
-Value Value::operator-(const Value& that) const {
- Value v;
- if (type != that.type) {
- ALOGE("Can't operate on different value types, %d, %d", type, that.type);
- return v;
- }
- if (type == STRING) {
- ALOGE("Can't operate on string value type");
- return v;
- }
-
- if (type == STORAGE) {
- ALOGE("Can't operate on storage value type");
- return v;
- }
-
- switch (type) {
- case INT:
- v.setInt(int_value - that.int_value);
- break;
- case LONG:
- v.setLong(long_value - that.long_value);
- break;
- case FLOAT:
- v.setFloat(float_value - that.float_value);
- break;
- case DOUBLE:
- v.setDouble(double_value - that.double_value);
- break;
- default:
- break;
- }
- return v;
-}
-
-Value& Value::operator=(const Value& that) {
- type = that.type;
- switch (type) {
- case INT:
- int_value = that.int_value;
- break;
- case LONG:
- long_value = that.long_value;
- break;
- case FLOAT:
- float_value = that.float_value;
- break;
- case DOUBLE:
- double_value = that.double_value;
- break;
- case STRING:
- str_value = that.str_value;
- break;
- case STORAGE:
- storage_value = that.storage_value;
- break;
- default:
- break;
- }
- return *this;
-}
-
-Value& Value::operator+=(const Value& that) {
- if (type != that.type) {
- ALOGE("Can't operate on different value types, %d, %d", type, that.type);
- return *this;
- }
- if (type == STRING) {
- ALOGE("Can't operate on string value type");
- return *this;
- }
- if (type == STORAGE) {
- ALOGE("Can't operate on storage value type");
- return *this;
- }
-
- switch (type) {
- case INT:
- int_value += that.int_value;
- break;
- case LONG:
- long_value += that.long_value;
- break;
- case FLOAT:
- float_value += that.float_value;
- break;
- case DOUBLE:
- double_value += that.double_value;
- break;
- default:
- break;
- }
- return *this;
-}
-
-double Value::getDouble() const {
- switch (type) {
- case INT:
- return int_value;
- case LONG:
- return long_value;
- case FLOAT:
- return float_value;
- case DOUBLE:
- return double_value;
- default:
- return 0;
- }
-}
-
-bool equalDimensions(const std::vector<Matcher>& dimension_a,
- const std::vector<Matcher>& dimension_b) {
- bool eq = dimension_a.size() == dimension_b.size();
- for (size_t i = 0; eq && i < dimension_a.size(); ++i) {
- if (dimension_b[i] != dimension_a[i]) {
- eq = false;
- }
- }
- return eq;
-}
-
-bool subsetDimensions(const std::vector<Matcher>& dimension_a,
- const std::vector<Matcher>& dimension_b) {
- if (dimension_a.size() > dimension_b.size()) {
- return false;
- }
- for (size_t i = 0; i < dimension_a.size(); ++i) {
- bool found = false;
- for (size_t j = 0; j < dimension_b.size(); ++j) {
- if (dimension_a[i] == dimension_b[j]) {
- found = true;
- }
- }
- if (!found) {
- return false;
- }
- }
- return true;
-}
-
-bool HasPositionANY(const FieldMatcher& matcher) {
- if (matcher.has_position() && matcher.position() == Position::ANY) {
- return true;
- }
- for (const auto& child : matcher.child()) {
- if (HasPositionANY(child)) {
- return true;
- }
- }
- return false;
-}
-
-bool HasPositionALL(const FieldMatcher& matcher) {
- if (matcher.has_position() && matcher.position() == Position::ALL) {
- return true;
- }
- for (const auto& child : matcher.child()) {
- if (HasPositionALL(child)) {
- return true;
- }
- }
- return false;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
deleted file mode 100644
index fd86e3683970..000000000000
--- a/cmds/statsd/src/FieldValue.h
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "annotations.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class HashableDimensionKey;
-struct Matcher;
-struct Field;
-struct FieldValue;
-
-const int32_t kMaxLogDepth = 2;
-const int32_t kLastBitMask = 0x80;
-const int32_t kClearLastBitDeco = 0x7f;
-const int32_t kClearAllPositionMatcherMask = 0xffff00ff;
-
-enum Type { UNKNOWN, INT, LONG, FLOAT, DOUBLE, STRING, STORAGE };
-
-int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
-
-int32_t encodeMatcherMask(int32_t mask[], int32_t depth);
-
-// Get the encoded field for a leaf with a [field] number at depth 0;
-inline int32_t getSimpleField(size_t field) {
- return ((int32_t)field << 8 * 2);
-}
-
-/**
- * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom
- * proto.
- * [mTag]: the atom id.
- * [mField]: encoded path from the root (atom) to leaf.
- *
- * For example:
- * WakeLockStateChanged {
- * repeated AttributionNode = 1;
- * int state = 2;
- * string tag = 3;
- * }
- * Read from logd, the items are structured as below:
- * [[[1000, "tag"], [2000, "tag2"],], 2,"hello"]
- *
- * When we read through the list, we will encode each field in a 32bit integer.
- * 8bit segments |--------|--------|--------|--------|
- * Depth field0 [L]field1 [L]field1
- *
- * The first 8 bits are the depth of the field. for example, the uid 1000 has depth 2.
- * The following 3 8-bit are for the item's position at each level.
- * The first bit of each 8bits field is reserved to mark if the item is the last item at that level
- * this is to make matching easier later.
- *
- * The above wakelock event is translated into FieldValue pairs.
- * 0x02010101->1000
- * 0x02010182->tag
- * 0x02018201->2000
- * 0x02018282->tag2
- * 0x00020000->2
- * 0x00030000->"hello"
- *
- * This encoding is the building block for the later operations.
- * Please see the definition for Matcher below to see how the matching is done.
- */
-struct Field {
-private:
- int32_t mTag;
- int32_t mField;
-
-public:
- Field() {}
-
- Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) {
- mField = getEncodedField(pos, depth, true);
- }
-
- Field(const Field& from) : mTag(from.getTag()), mField(from.getField()) {
- }
-
- Field(int32_t tag, int32_t field) : mTag(tag), mField(field){};
-
- inline void setField(int32_t field) {
- mField = field;
- }
-
- inline void setTag(int32_t tag) {
- mTag = tag;
- }
-
- inline void decorateLastPos(int32_t depth) {
- int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
- mField |= mask;
- }
-
- inline int32_t getTag() const {
- return mTag;
- }
-
- inline int32_t getDepth() const {
- return (mField >> 24);
- }
-
- inline int32_t getPath(int32_t depth) const {
- if (depth > 2 || depth < 0) return 0;
-
- int32_t field = (mField & 0x00ffffff);
- int32_t mask = 0xffffffff;
- return (field & (mask << 8 * (kMaxLogDepth - depth)));
- }
-
- inline int32_t getPrefix(int32_t depth) const {
- if (depth == 0) return 0;
- return getPath(depth - 1);
- }
-
- inline int32_t getField() const {
- return mField;
- }
-
- inline int32_t getRawPosAtDepth(int32_t depth) const {
- int32_t field = (mField & 0x00ffffff);
- int32_t shift = 8 * (kMaxLogDepth - depth);
- int32_t mask = 0xff << shift;
-
- return (field & mask) >> shift;
- }
-
- inline int32_t getPosAtDepth(int32_t depth) const {
- return getRawPosAtDepth(depth) & kClearLastBitDeco;
- }
-
- // Check if the first bit of the 8-bit segment for depth is 1
- inline bool isLastPos(int32_t depth) const {
- int32_t field = (mField & 0x00ffffff);
- int32_t mask = kLastBitMask << 8 * (kMaxLogDepth - depth);
- return (field & mask) != 0;
- }
-
- // if the 8-bit segment is all 0's
- inline bool isAnyPosMatcher(int32_t depth) const {
- return getDepth() >= depth && getRawPosAtDepth(depth) == 0;
- }
- // if the 8bit is 0x80 (1000 0000)
- inline bool isLastPosMatcher(int32_t depth) const {
- return getDepth() >= depth && getRawPosAtDepth(depth) == kLastBitMask;
- }
-
- inline bool operator==(const Field& that) const {
- return mTag == that.getTag() && mField == that.getField();
- };
-
- inline bool operator!=(const Field& that) const {
- return mTag != that.getTag() || mField != that.getField();
- };
-
- bool operator<(const Field& that) const {
- if (mTag != that.getTag()) {
- return mTag < that.getTag();
- }
-
- if (mField != that.getField()) {
- return mField < that.getField();
- }
-
- return false;
- }
-
- bool matches(const Matcher& that) const;
-};
-
-/**
- * Matcher represents a leaf matcher in the FieldMatcher in statsd_config.
- *
- * It contains all information needed to match one or more leaf node.
- * All information is encoded in a Field(2 ints) and a bit mask(1 int).
- *
- * For example, to match the first/any/last uid field in attribution chain in Atom 10,
- * we have the following FieldMatcher in statsd_config
- * FieldMatcher {
- * field:10
- * FieldMatcher {
- * field:1
- * position: any/last/first
- * FieldMatcher {
- * field:1
- * }
- * }
- * }
- *
- * We translate the FieldMatcher into a Field, and mask
- * First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f
- * Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f
- * Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f
- * All: [Matcher Field] 0x02010001 [Mask]0xff7f7f7f
- *
- * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if
- * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
- * equal. Nothing can beat the performance of this matching algorithm.
- *
- * TODO(b/110561213): ADD EXAMPLE HERE.
- */
-struct Matcher {
- Matcher(const Field& matcher, int32_t mask) : mMatcher(matcher), mMask(mask){};
-
- const Field mMatcher;
- const int32_t mMask;
-
- inline const Field& getMatcher() const {
- return mMatcher;
- }
-
- inline int32_t getMask() const {
- return mMask;
- }
-
- inline int32_t getRawMaskAtDepth(int32_t depth) const {
- int32_t field = (mMask & 0x00ffffff);
- int32_t shift = 8 * (kMaxLogDepth - depth);
- int32_t mask = 0xff << shift;
-
- return (field & mask) >> shift;
- }
-
- bool hasAllPositionMatcher() const {
- return mMatcher.getDepth() == 2 && getRawMaskAtDepth(1) == 0x7f;
- }
-
- bool hasAnyPositionMatcher(int* prefix) const {
- if (mMatcher.getDepth() == 2 && mMatcher.getRawPosAtDepth(1) == 0) {
- (*prefix) = mMatcher.getPrefix(1);
- return true;
- }
- return false;
- }
-
- inline bool operator!=(const Matcher& that) const {
- return mMatcher != that.getMatcher() || mMask != that.getMask();
- }
-
- inline bool operator==(const Matcher& that) const {
- return mMatcher == that.mMatcher && mMask == that.mMask;
- }
-};
-
-inline Matcher getSimpleMatcher(int32_t tag, size_t field) {
- return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
-}
-
-inline Matcher getFirstUidMatcher(int32_t atomId) {
- int32_t pos[] = {1, 1, 1};
- return Matcher(Field(atomId, pos, 2), 0xff7f7f7f);
-}
-
-/**
- * A wrapper for a union type to contain multiple types of values.
- *
- */
-struct Value {
- Value() : type(UNKNOWN) {}
-
- Value(int32_t v) {
- int_value = v;
- type = INT;
- }
-
- Value(int64_t v) {
- long_value = v;
- type = LONG;
- }
-
- Value(float v) {
- float_value = v;
- type = FLOAT;
- }
-
- Value(double v) {
- double_value = v;
- type = DOUBLE;
- }
-
- Value(const std::string& v) {
- str_value = v;
- type = STRING;
- }
-
- Value(const std::vector<uint8_t>& v) {
- storage_value = v;
- type = STORAGE;
- }
-
- void setInt(int32_t v) {
- int_value = v;
- type = INT;
- }
-
- void setLong(int64_t v) {
- long_value = v;
- type = LONG;
- }
-
- void setFloat(float v) {
- float_value = v;
- type = FLOAT;
- }
-
- void setDouble(double v) {
- double_value = v;
- type = DOUBLE;
- }
-
- union {
- int32_t int_value;
- int64_t long_value;
- float float_value;
- double double_value;
- };
- std::string str_value;
- std::vector<uint8_t> storage_value;
-
- Type type;
-
- std::string toString() const;
-
- bool isZero() const;
-
- Type getType() const {
- return type;
- }
-
- double getDouble() const;
-
- Value(const Value& from);
-
- bool operator==(const Value& that) const;
- bool operator!=(const Value& that) const;
-
- bool operator<(const Value& that) const;
- bool operator>(const Value& that) const;
- bool operator>=(const Value& that) const;
- Value operator-(const Value& that) const;
- Value& operator+=(const Value& that);
- Value& operator=(const Value& that);
-};
-
-class Annotations {
-public:
- Annotations() {
- setNested(true); // Nested = true by default
- }
-
- // This enum stores where particular annotations can be found in the
- // bitmask. Note that these pos do not correspond to annotation ids.
- enum {
- NESTED_POS = 0x0,
- PRIMARY_POS = 0x1,
- EXCLUSIVE_POS = 0x2,
- UID_POS = 0x3
- };
-
- inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); }
-
- inline void setPrimaryField(bool primary) { setBitmaskAtPos(PRIMARY_POS, primary); }
-
- inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); }
-
- inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
-
- // Default value = false
- inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
-
- // Default value = false
- inline bool isPrimaryField() const { return getValueFromBitmask(PRIMARY_POS); }
-
- // Default value = false
- inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); }
-
- // Default value = false
- inline bool isUidField() const { return getValueFromBitmask(UID_POS); }
-
-private:
- inline void setBitmaskAtPos(int pos, bool value) {
- mBooleanBitmask &= ~(1 << pos); // clear
- mBooleanBitmask |= (value << pos); // set
- }
-
- inline bool getValueFromBitmask(int pos) const {
- return (mBooleanBitmask >> pos) & 0x1;
- }
-
- // This is a bitmask over all annotations stored in boolean form. Because
- // there are only 4 booleans, just one byte is required.
- uint8_t mBooleanBitmask = 0;
-};
-
-/**
- * Represents a log item, or a dimension item (They are essentially the same).
- */
-struct FieldValue {
- FieldValue() {}
- FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) {
- }
- bool operator==(const FieldValue& that) const {
- return mField == that.mField && mValue == that.mValue;
- }
- bool operator!=(const FieldValue& that) const {
- return mField != that.mField || mValue != that.mValue;
- }
- bool operator<(const FieldValue& that) const {
- if (mField != that.mField) {
- return mField < that.mField;
- }
-
- if (mValue != that.mValue) {
- return mValue < that.mValue;
- }
-
- return false;
- }
-
- Field mField;
- Value mValue;
- Annotations mAnnotations;
-};
-
-bool HasPositionANY(const FieldMatcher& matcher);
-bool HasPositionALL(const FieldMatcher& matcher);
-
-bool isAttributionUidField(const FieldValue& value);
-
-/* returns uid if the field is uid field, or -1 if the field is not a uid field */
-int getUidIfExists(const FieldValue& value);
-
-void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
-
-bool isAttributionUidField(const Field& field, const Value& value);
-bool isUidField(const FieldValue& fieldValue);
-
-bool equalDimensions(const std::vector<Matcher>& dimension_a,
- const std::vector<Matcher>& dimension_b);
-
-// Returns true if dimension_a is a subset of dimension_b.
-bool subsetDimensions(const std::vector<Matcher>& dimension_a,
- const std::vector<Matcher>& dimension_b);
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
deleted file mode 100644
index eba66e0cb7b0..000000000000
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ /dev/null
@@ -1,381 +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.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "HashableDimensionKey.h"
-#include "FieldValue.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::string;
-using std::vector;
-using android::base::StringPrintf;
-
-// These constants must be kept in sync with those in StatsDimensionsValue.java
-const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
-const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
-const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4;
-// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because
-// unused -- statsd does not correctly support bool types)
-const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
-const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
-
-/**
- * Recursive helper function that populates a parent StatsDimensionsValueParcel
- * with children StatsDimensionsValueParcels.
- *
- * \param parent parcel that will be populated with children
- * \param childDepth depth of children FieldValues
- * \param childPrefix expected FieldValue prefix of children
- * \param dims vector of FieldValues stored by HashableDimensionKey
- * \param index position in dims to start reading children from
- */
-static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent,
- int childDepth, int childPrefix,
- const vector<FieldValue>& dims,
- size_t& index) {
- if (childDepth > 2) {
- ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel.");
- return;
- }
-
- while (index < dims.size()) {
- const FieldValue& dim = dims[index];
- int fieldDepth = dim.mField.getDepth();
- int fieldPrefix = dim.mField.getPrefix(childDepth);
-
- StatsDimensionsValueParcel child;
- child.field = dim.mField.getPosAtDepth(childDepth);
-
- if (fieldDepth == childDepth && fieldPrefix == childPrefix) {
- switch (dim.mValue.getType()) {
- case INT:
- child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE;
- child.intValue = dim.mValue.int_value;
- break;
- case LONG:
- child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE;
- child.longValue = dim.mValue.long_value;
- break;
- case FLOAT:
- child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE;
- child.floatValue = dim.mValue.float_value;
- break;
- case STRING:
- child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
- child.stringValue = dim.mValue.str_value;
- break;
- default:
- ALOGE("Encountered FieldValue with unsupported value type.");
- break;
- }
- index++;
- parent.tupleValue.push_back(child);
- } else if (fieldDepth > childDepth && fieldPrefix == childPrefix) {
- // This FieldValue is not a child of the current parent, but it is
- // an indirect descendant. Thus, create a direct child of TUPLE_TYPE
- // and recurse to parcel the indirect descendants.
- child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
- populateStatsDimensionsValueParcelChildren(child, childDepth + 1,
- dim.mField.getPrefix(childDepth + 1), dims,
- index);
- parent.tupleValue.push_back(child);
- } else {
- return;
- }
- }
-}
-
-StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const {
- StatsDimensionsValueParcel root;
- if (mValues.size() == 0) {
- return root;
- }
-
- root.field = mValues[0].mField.getTag();
- root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
-
- // Children of the root correspond to top-level (depth = 0) FieldValues.
- int childDepth = 0;
- int childPrefix = 0;
- size_t index = 0;
- populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index);
-
- return root;
-}
-
-android::hash_t hashDimension(const HashableDimensionKey& value) {
- android::hash_t hash = 0;
- for (const auto& fieldValue : value.getValues()) {
- hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField()));
- hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag()));
- hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType()));
- switch (fieldValue.mValue.getType()) {
- case INT:
- hash = android::JenkinsHashMix(hash,
- android::hash_type(fieldValue.mValue.int_value));
- break;
- case LONG:
- hash = android::JenkinsHashMix(hash,
- android::hash_type(fieldValue.mValue.long_value));
- break;
- case STRING:
- hash = android::JenkinsHashMix(hash, static_cast<uint32_t>(std::hash<std::string>()(
- fieldValue.mValue.str_value)));
- break;
- case FLOAT: {
- hash = android::JenkinsHashMix(hash,
- android::hash_type(fieldValue.mValue.float_value));
- break;
- }
- default:
- break;
- }
- }
- return JenkinsHashWhiten(hash);
-}
-
-bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values,
- FieldValue* output) {
- for (const auto& value : values) {
- if (value.mField.matches(matcherField)) {
- (*output) = value;
- return true;
- }
- }
- return false;
-}
-
-bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
- HashableDimensionKey* output) {
- size_t num_matches = 0;
- for (const auto& value : values) {
- for (size_t i = 0; i < matcherFields.size(); ++i) {
- const auto& matcher = matcherFields[i];
- if (value.mField.matches(matcher)) {
- output->addValue(value);
- output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
- output->mutableValue(num_matches)->mField.setField(
- value.mField.getField() & matcher.mMask);
- num_matches++;
- }
- }
- }
- return num_matches > 0;
-}
-
-bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) {
- size_t num_matches = 0;
- const int32_t simpleFieldMask = 0xff7f0000;
- const int32_t attributionUidFieldMask = 0xff7f7f7f;
- for (const auto& value : values) {
- if (value.mAnnotations.isPrimaryField()) {
- output->addValue(value);
- output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
- const int32_t mask =
- isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask;
- output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask);
- num_matches++;
- }
- }
- return num_matches > 0;
-}
-
-void filterGaugeValues(const std::vector<Matcher>& matcherFields,
- const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
- for (const auto& field : matcherFields) {
- for (const auto& value : values) {
- if (value.mField.matches(field)) {
- output->push_back(value);
- }
- }
- }
-}
-
-void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
- const Metric2Condition& links,
- HashableDimensionKey* conditionDimension) {
- // Get the dimension first by using dimension from what.
- filterValues(links.metricFields, eventValues, conditionDimension);
-
- size_t count = conditionDimension->getValues().size();
- if (count != links.conditionFields.size()) {
- return;
- }
-
- for (size_t i = 0; i < count; i++) {
- conditionDimension->mutableValue(i)->mField.setField(
- links.conditionFields[i].mMatcher.getField());
- conditionDimension->mutableValue(i)->mField.setTag(
- links.conditionFields[i].mMatcher.getTag());
- }
-}
-
-void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
- HashableDimensionKey* statePrimaryKey) {
- // First, get the dimension from the event using the "what" fields from the
- // MetricStateLinks.
- filterValues(link.metricFields, eventValues, statePrimaryKey);
-
- // Then check that the statePrimaryKey size equals the number of state fields
- size_t count = statePrimaryKey->getValues().size();
- if (count != link.stateFields.size()) {
- return;
- }
-
- // For each dimension Value in the statePrimaryKey, set the field and tag
- // using the state atom fields from MetricStateLinks.
- for (size_t i = 0; i < count; i++) {
- statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField());
- statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag());
- }
-}
-
-bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
- const HashableDimensionKey& primaryKey,
- const vector<Metric2State>& stateLinks, const int32_t stateAtomId) {
- if (whatKey.getValues().size() < primaryKey.getValues().size()) {
- ALOGE("Contains linked values false: whatKey is too small");
- return false;
- }
-
- for (const auto& primaryValue : primaryKey.getValues()) {
- bool found = false;
- for (const auto& whatValue : whatKey.getValues()) {
- if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) &&
- primaryValue.mValue == whatValue.mValue) {
- found = true;
- break;
- }
- }
- if (!found) {
- return false;
- }
- }
- return true;
-}
-
-bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId,
- const Field& stateField, const Field& metricField) {
- for (auto stateLink : stateLinks) {
- if (stateLink.stateAtomId != stateAtomId) {
- continue;
- }
-
- for (size_t i = 0; i < stateLink.stateFields.size(); i++) {
- if (stateLink.stateFields[i].mMatcher == stateField &&
- stateLink.metricFields[i].mMatcher == metricField) {
- return true;
- }
- }
- }
- return false;
-}
-
-bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
- if (s1.size() != s2.size()) {
- return s1.size() < s2.size();
- }
-
- size_t count = s1.size();
- for (size_t i = 0; i < count; i++) {
- if (s1[i] != s2[i]) {
- return s1[i] < s2[i];
- }
- }
- return false;
-}
-
-bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const {
- return !((*this) == that);
-}
-
-bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
- if (mValues.size() != that.getValues().size()) {
- return false;
- }
- size_t count = mValues.size();
- for (size_t i = 0; i < count; i++) {
- if (mValues[i] != (that.getValues())[i]) {
- return false;
- }
- }
- return true;
-};
-
-bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
- return LessThan(getValues(), that.getValues());
-};
-
-bool HashableDimensionKey::contains(const HashableDimensionKey& that) const {
- if (mValues.size() < that.getValues().size()) {
- return false;
- }
-
- if (mValues.size() == that.getValues().size()) {
- return (*this) == that;
- }
-
- for (const auto& value : that.getValues()) {
- bool found = false;
- for (const auto& myValue : mValues) {
- if (value.mField == myValue.mField && value.mValue == myValue.mValue) {
- found = true;
- break;
- }
- }
- if (!found) {
- return false;
- }
- }
-
- return true;
-}
-
-string HashableDimensionKey::toString() const {
- std::string output;
- for (const auto& value : mValues) {
- output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(),
- value.mValue.toString().c_str());
- }
- return output;
-}
-
-bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
- return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
- mStateValuesKey == that.getStateValuesKey();
-};
-
-string MetricDimensionKey::toString() const {
- return mDimensionKeyInWhat.toString() + mStateValuesKey.toString();
-}
-
-bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
- if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) {
- return true;
- } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) {
- return false;
- }
-
- return mStateValuesKey < that.getStateValuesKey();
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
deleted file mode 100644
index bd011005a301..000000000000
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ /dev/null
@@ -1,238 +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.
- */
-
-#pragma once
-
-#include <aidl/android/os/StatsDimensionsValueParcel.h>
-#include <utils/JenkinsHash.h>
-#include <vector>
-#include "android-base/stringprintf.h"
-#include "FieldValue.h"
-#include "logd/LogEvent.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using ::aidl::android::os::StatsDimensionsValueParcel;
-
-struct Metric2Condition {
- int64_t conditionId;
- std::vector<Matcher> metricFields;
- std::vector<Matcher> conditionFields;
-};
-
-struct Metric2State {
- int32_t stateAtomId;
- std::vector<Matcher> metricFields;
- std::vector<Matcher> stateFields;
-};
-
-class HashableDimensionKey {
-public:
- explicit HashableDimensionKey(const std::vector<FieldValue>& values) {
- mValues = values;
- }
-
- HashableDimensionKey() {};
-
- HashableDimensionKey(const HashableDimensionKey& that) : mValues(that.getValues()){};
-
- inline void addValue(const FieldValue& value) {
- mValues.push_back(value);
- }
-
- inline const std::vector<FieldValue>& getValues() const {
- return mValues;
- }
-
- inline std::vector<FieldValue>* mutableValues() {
- return &mValues;
- }
-
- inline FieldValue* mutableValue(size_t i) {
- if (i >= 0 && i < mValues.size()) {
- return &(mValues[i]);
- }
- return nullptr;
- }
-
- StatsDimensionsValueParcel toStatsDimensionsValueParcel() const;
-
- std::string toString() const;
-
- bool operator!=(const HashableDimensionKey& that) const;
-
- bool operator==(const HashableDimensionKey& that) const;
-
- bool operator<(const HashableDimensionKey& that) const;
-
- bool contains(const HashableDimensionKey& that) const;
-
-private:
- std::vector<FieldValue> mValues;
-};
-
-class MetricDimensionKey {
-public:
- explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
- const HashableDimensionKey& stateValuesKey)
- : mDimensionKeyInWhat(dimensionKeyInWhat), mStateValuesKey(stateValuesKey){};
-
- MetricDimensionKey(){};
-
- MetricDimensionKey(const MetricDimensionKey& that)
- : mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
- mStateValuesKey(that.getStateValuesKey()){};
-
- MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
-
- std::string toString() const;
-
- inline const HashableDimensionKey& getDimensionKeyInWhat() const {
- return mDimensionKeyInWhat;
- }
-
- inline const HashableDimensionKey& getStateValuesKey() const {
- return mStateValuesKey;
- }
-
- inline HashableDimensionKey* getMutableStateValuesKey() {
- return &mStateValuesKey;
- }
-
- inline void setStateValuesKey(const HashableDimensionKey& key) {
- mStateValuesKey = key;
- }
-
- bool hasStateValuesKey() const {
- return mStateValuesKey.getValues().size() > 0;
- }
-
- bool operator==(const MetricDimensionKey& that) const;
-
- bool operator<(const MetricDimensionKey& that) const;
-
-private:
- HashableDimensionKey mDimensionKeyInWhat;
- HashableDimensionKey mStateValuesKey;
-};
-
-android::hash_t hashDimension(const HashableDimensionKey& key);
-
-/**
- * Returns true if a FieldValue field matches the matcher field.
- * The value of the FieldValue is output.
- */
-bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values,
- FieldValue* output);
-
-/**
- * Creating HashableDimensionKeys from FieldValues using matcher.
- *
- * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
- * in it. This is because: for example, when we create dimension from last uid in attribution chain,
- * In one event, uid 1000 is at position 5 and it's the last
- * In another event, uid 1000 is at position 6, and it's the last
- * these 2 events should be mapped to the same dimension. So we will remove the original position
- * from the dimension key for the uid field (by applying 0x80 bit mask).
- */
-bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<FieldValue>& values,
- HashableDimensionKey* output);
-
-/**
- * Creating HashableDimensionKeys from State Primary Keys in FieldValues.
- *
- * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
- * in it. This is because: for example, when we create dimension from last uid in attribution chain,
- * In one event, uid 1000 is at position 5 and it's the last
- * In another event, uid 1000 is at position 6, and it's the last
- * these 2 events should be mapped to the same dimension. So we will remove the original position
- * from the dimension key for the uid field (by applying 0x80 bit mask).
- */
-bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output);
-
-/**
- * Filter the values from FieldValues using the matchers.
- *
- * In contrast to the above function, this function will not do any modification to the original
- * data. Considering it as taking a snapshot on the atom event.
- */
-void filterGaugeValues(const std::vector<Matcher>& matchers, const std::vector<FieldValue>& values,
- std::vector<FieldValue>* output);
-
-void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
- const Metric2Condition& links,
- HashableDimensionKey* conditionDimension);
-
-/**
- * Get dimension values using metric's "what" fields and fill statePrimaryKey's
- * mField information using "state" fields.
- */
-void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
- HashableDimensionKey* statePrimaryKey);
-
-/**
- * Returns true if the primaryKey values are a subset of the whatKey values.
- * The values from the primaryKey come from the state atom, so we need to
- * check that a link exists between the state atom field and what atom field.
- *
- * Example:
- * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
- * statePrimaryKey = [Atom: 27, {uid: 1005}]
- * Returns true IF one of the Metric2State links Atom 10's uid to Atom 27's uid
- *
- * Example:
- * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
- * statePrimaryKey = [Atom: 59, {uid: 1005, package_name: "system"}]
- * Returns false
- */
-bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
- const HashableDimensionKey& primaryKey,
- const std::vector<Metric2State>& stateLinks,
- const int32_t stateAtomId);
-
-/**
- * Returns true if there is a Metric2State link that links the stateField and
- * the metricField (they are equal fields from different atoms).
- */
-bool linked(const std::vector<Metric2State>& stateLinks, const int32_t stateAtomId,
- const Field& stateField, const Field& metricField);
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-namespace std {
-
-using android::os::statsd::HashableDimensionKey;
-using android::os::statsd::MetricDimensionKey;
-
-template <>
-struct hash<HashableDimensionKey> {
- std::size_t operator()(const HashableDimensionKey& key) const {
- return hashDimension(key);
- }
-};
-
-template <>
-struct hash<MetricDimensionKey> {
- std::size_t operator()(const MetricDimensionKey& key) const {
- android::hash_t hash = hashDimension(key.getDimensionKeyInWhat());
- hash = android::JenkinsHashMix(hash, hashDimension(key.getStateValuesKey()));
- return android::JenkinsHashWhiten(hash);
- }
-};
-} // namespace std
diff --git a/cmds/statsd/src/Log.h b/cmds/statsd/src/Log.h
deleted file mode 100644
index 87f4cbaf02f6..000000000000
--- a/cmds/statsd/src/Log.h
+++ /dev/null
@@ -1,33 +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.
- */
-
-/*
- * This file must be included at the top of the file. Other header files
- * occasionally include log.h, and if LOG_TAG isn't set when that happens
- * we'll get a preprocesser error when we try to define it here.
- */
-
-#pragma once
-
-#define LOG_TAG "statsd"
-
-#include <log/log.h>
-
-// Use the local value to turn on/off debug logs instead of using log.tag. properties.
-// The advantage is that in production compiler can remove the logging code if the local
-// DEBUG/VERBOSE is false.
-#define VLOG(...) \
- if (DEBUG) ALOGD(__VA_ARGS__);
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
deleted file mode 100644
index 6eff639aca92..000000000000
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ /dev/null
@@ -1,1145 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StatsLogProcessor.h"
-
-#include <android-base/file.h>
-#include <cutils/multiuser.h>
-#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
-#include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h>
-
-#include "android-base/stringprintf.h"
-#include "external/StatsPullerManager.h"
-#include "guardrail/StatsdStats.h"
-#include "logd/LogEvent.h"
-#include "metrics/CountMetricProducer.h"
-#include "StatsService.h"
-#include "state/StateManager.h"
-#include "stats_log_util.h"
-#include "stats_util.h"
-#include "statslog_statsd.h"
-#include "storage/StorageManager.h"
-
-using namespace android;
-using android::base::StringPrintf;
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_FLOAT;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for ConfigMetricsReportList
-const int FIELD_ID_CONFIG_KEY = 1;
-const int FIELD_ID_REPORTS = 2;
-// for ConfigKey
-const int FIELD_ID_UID = 1;
-const int FIELD_ID_ID = 2;
-// for ConfigMetricsReport
-// const int FIELD_ID_METRICS = 1; // written in MetricsManager.cpp
-const int FIELD_ID_UID_MAP = 2;
-const int FIELD_ID_LAST_REPORT_ELAPSED_NANOS = 3;
-const int FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS = 4;
-const int FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS = 5;
-const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6;
-const int FIELD_ID_DUMP_REPORT_REASON = 8;
-const int FIELD_ID_STRINGS = 9;
-
-// for ActiveConfigList
-const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
-
-// for permissions checks
-constexpr const char* kPermissionDump = "android.permission.DUMP";
-constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
-
-#define NS_PER_HOUR 3600 * NS_PER_SEC
-
-#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric"
-#define STATS_METADATA_DIR "/data/misc/stats-metadata"
-
-// Cool down period for writing data to disk to avoid overwriting files.
-#define WRITE_DATA_COOL_DOWN_SEC 5
-
-StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor,
- const int64_t timeBaseNs,
- const std::function<bool(const ConfigKey&)>& sendBroadcast,
- const std::function<bool(
- const int&, const vector<int64_t>&)>& activateBroadcast)
- : mUidMap(uidMap),
- mPullerManager(pullerManager),
- mAnomalyAlarmMonitor(anomalyAlarmMonitor),
- mPeriodicAlarmMonitor(periodicAlarmMonitor),
- mSendBroadcast(sendBroadcast),
- mSendActivationBroadcast(activateBroadcast),
- mTimeBaseNs(timeBaseNs),
- mLargestTimestampSeen(0),
- mLastTimestampSeen(0) {
- mPullerManager->ForceClearPullerCache();
- StateManager::getInstance().updateLogSources(uidMap);
-}
-
-StatsLogProcessor::~StatsLogProcessor() {
-}
-
-static void flushProtoToBuffer(ProtoOutputStream& proto, vector<uint8_t>* outData) {
- outData->clear();
- outData->resize(proto.size());
- size_t pos = 0;
- sp<android::util::ProtoReader> reader = proto.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((*outData)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-}
-
-void StatsLogProcessor::processFiredAnomalyAlarmsLocked(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
- for (const auto& itr : mMetricsManagers) {
- itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
- }
-}
-void StatsLogProcessor::onPeriodicAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet) {
-
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- for (const auto& itr : mMetricsManagers) {
- itr.second->onPeriodicAlarmFired(timestampNs, alarmSet);
- }
-}
-
-void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
- if (std::pair<int, int> indexRange; event->hasAttributionChain(&indexRange)) {
- vector<FieldValue>* const fieldValues = event->getMutableValues();
- for (int i = indexRange.first; i <= indexRange.second; i++) {
- FieldValue& fieldValue = fieldValues->at(i);
- if (isAttributionUidField(fieldValue)) {
- const int hostUid = mUidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
- fieldValue.mValue.setInt(hostUid);
- }
- }
- } else {
- int uidFieldIndex = event->getUidFieldIndex();
- if (uidFieldIndex != -1) {
- Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
- const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
- value.setInt(hostUid);
- }
- }
-}
-
-void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) {
- status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
- bool is_create = event.GetBool(3, &err);
- auto parent_uid = int(event.GetLong(1, &err2));
- auto isolated_uid = int(event.GetLong(2, &err3));
- if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
- if (is_create) {
- mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
- } else {
- mUidMap->removeIsolatedUid(isolated_uid);
- }
- } else {
- ALOGE("Failed to parse uid in the isolated uid change event.");
- }
-}
-
-void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) {
- pid_t pid = event->GetPid();
- uid_t uid = event->GetUid();
- if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
- !checkPermissionForIds(kPermissionUsage, pid, uid)) {
- return;
- }
- // The Get* functions don't modify the status on success, they only write in
- // failure statuses, so we can use one status variable for all calls then
- // check if it is no longer NO_ERROR.
- status_t err = NO_ERROR;
- InstallTrainInfo trainInfo;
- trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err));
- trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err);
- trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err);
- trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err);
- trainInfo.requiresLowLatencyMonitor =
- event->GetBool(5 /*requires low latency monitor field id*/, &err);
- trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err));
- std::vector<uint8_t> trainExperimentIdBytes =
- event->GetStorage(7 /*experiment ids field id*/, &err);
- bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err);
-
- if (err != NO_ERROR) {
- ALOGE("Failed to parse fields in binary push state changed log event");
- return;
- }
- ExperimentIds trainExperimentIds;
- if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(),
- trainExperimentIdBytes.size())) {
- ALOGE("Failed to parse experimentids in binary push state changed.");
- return;
- }
- trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(),
- trainExperimentIds.experiment_id().end()};
-
- // Update the train info on disk and get any data the logevent is missing.
- getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo);
-
- std::vector<uint8_t> trainExperimentIdProto;
- writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto);
- int32_t userId = multiuser_get_user_id(uid);
-
- event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG);
- event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE);
- event->updateValue(8 /*user id field id*/, userId, INT);
-
- // If this event is a rollback event, then the following bits in the event
- // are invalid and we will need to update them with the values we pulled
- // from disk.
- if (is_rollback) {
- int bit = trainInfo.requiresStaging ? 1 : 0;
- event->updateValue(3 /*requires staging field id*/, bit, INT);
- bit = trainInfo.rollbackEnabled ? 1 : 0;
- event->updateValue(4 /*rollback enabled field id*/, bit, INT);
- bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0;
- event->updateValue(5 /*requires low latency monitor field id*/, bit, INT);
- }
-}
-
-void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback,
- InstallTrainInfo* trainInfo) {
- // If the train name is empty, we don't know which train to attribute the
- // event to, so return early.
- if (trainInfo->trainName.empty()) {
- return;
- }
- bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfoOnDisk;
- readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk);
-
- bool resetExperimentIds = false;
- if (readTrainInfoSuccess) {
- // Keep the old train version if we received an empty version.
- if (trainInfo->trainVersionCode == -1) {
- trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode;
- } else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) {
- // Reset experiment ids if we receive a new non-empty train version.
- resetExperimentIds = true;
- }
-
- // Reset if we received a different experiment id.
- if (!trainInfo->experimentIds.empty() &&
- (trainInfoOnDisk.experimentIds.empty() ||
- trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) {
- resetExperimentIds = true;
- }
- }
-
- // Find the right experiment IDs
- if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) {
- trainInfo->experimentIds = trainInfoOnDisk.experimentIds;
- }
-
- if (!trainInfo->experimentIds.empty()) {
- int64_t firstId = trainInfo->experimentIds.at(0);
- auto& ids = trainInfo->experimentIds;
- switch (trainInfo->status) {
- case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
- if (find(ids.begin(), ids.end(), firstId + 1) == ids.end()) {
- ids.push_back(firstId + 1);
- }
- break;
- case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
- if (find(ids.begin(), ids.end(), firstId + 2) == ids.end()) {
- ids.push_back(firstId + 2);
- }
- break;
- case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
- if (find(ids.begin(), ids.end(), firstId + 3) == ids.end()) {
- ids.push_back(firstId + 3);
- }
- break;
- }
- }
-
- // If this event is a rollback event, the following fields are invalid and
- // need to be replaced by the fields stored to disk.
- if (is_rollback) {
- trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging;
- trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled;
- trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor;
- }
-
- StorageManager::writeTrainInfo(*trainInfo);
-}
-
-void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) {
- pid_t pid = event->GetPid();
- uid_t uid = event->GetUid();
- if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
- !checkPermissionForIds(kPermissionUsage, pid, uid)) {
- return;
- }
- // The Get* functions don't modify the status on success, they only write in
- // failure statuses, so we can use one status variable for all calls then
- // check if it is no longer NO_ERROR.
- status_t err = NO_ERROR;
- int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err));
- string packageName = string(event->GetString(2 /*package name field id*/, &err));
-
- if (err != NO_ERROR) {
- ALOGE("Failed to parse fields in watchdog rollback occurred log event");
- return;
- }
-
- vector<int64_t> experimentIds =
- processWatchdogRollbackOccurred(rollbackType, packageName);
- vector<uint8_t> experimentIdProto;
- writeExperimentIdsToProto(experimentIds, &experimentIdProto);
-
- event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE);
-}
-
-vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
- const string& packageNameIn) {
- // If the package name is empty, we can't attribute it to any train, so
- // return early.
- if (packageNameIn.empty()) {
- return vector<int64_t>();
- }
- bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfoOnDisk;
- // We use the package name of the event as the train name.
- readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk);
-
- if (!readTrainInfoSuccess) {
- return vector<int64_t>();
- }
-
- if (trainInfoOnDisk.experimentIds.empty()) {
- return vector<int64_t>();
- }
-
- int64_t firstId = trainInfoOnDisk.experimentIds[0];
- auto& ids = trainInfoOnDisk.experimentIds;
- switch (rollbackTypeIn) {
- case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
- if (find(ids.begin(), ids.end(), firstId + 4) == ids.end()) {
- ids.push_back(firstId + 4);
- }
- StorageManager::writeTrainInfo(trainInfoOnDisk);
- break;
- case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
- if (find(ids.begin(), ids.end(), firstId + 5) == ids.end()) {
- ids.push_back(firstId + 5);
- }
- StorageManager::writeTrainInfo(trainInfoOnDisk);
- break;
- }
-
- return trainInfoOnDisk.experimentIds;
-}
-
-void StatsLogProcessor::resetConfigs() {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- resetConfigsLocked(getElapsedRealtimeNs());
-}
-
-void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) {
- std::vector<ConfigKey> configKeys;
- for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
- configKeys.push_back(it->first);
- }
- resetConfigsLocked(timestampNs, configKeys);
-}
-
-void StatsLogProcessor::OnLogEvent(LogEvent* event) {
- OnLogEvent(event, getElapsedRealtimeNs());
-}
-
-void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
-
- // Tell StatsdStats about new event
- const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs();
- int atomId = event->GetTagId();
- StatsdStats::getInstance().noteAtomLogged(atomId, eventElapsedTimeNs / NS_PER_SEC);
- if (!event->isValid()) {
- StatsdStats::getInstance().noteAtomError(atomId);
- return;
- }
-
- // Hard-coded logic to update train info on disk and fill in any information
- // this log event may be missing.
- if (atomId == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) {
- onBinaryPushStateChangedEventLocked(event);
- }
-
- // Hard-coded logic to update experiment ids on disk for certain rollback
- // types and fill the rollback atom with experiment ids
- if (atomId == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) {
- onWatchdogRollbackOccurredLocked(event);
- }
-
- if (mPrintAllLogs) {
- ALOGI("%s", event->ToString().c_str());
- }
- resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
-
- // Hard-coded logic to update the isolated uid's in the uid-map.
- // The field numbers need to be currently updated by hand with atoms.proto
- if (atomId == android::os::statsd::util::ISOLATED_UID_CHANGED) {
- onIsolatedUidChangedEventLocked(*event);
- } else {
- // Map the isolated uid to host uid if necessary.
- mapIsolatedUidToHostUidIfNecessaryLocked(event);
- }
-
- StateManager::getInstance().onLogEvent(*event);
-
- if (mMetricsManagers.empty()) {
- return;
- }
-
- bool fireAlarm = false;
- {
- std::lock_guard<std::mutex> anomalyLock(mAnomalyAlarmMutex);
- if (mNextAnomalyAlarmTime != 0 &&
- MillisToNano(mNextAnomalyAlarmTime) <= elapsedRealtimeNs) {
- mNextAnomalyAlarmTime = 0;
- VLOG("informing anomaly alarm at time %lld", (long long)elapsedRealtimeNs);
- fireAlarm = true;
- }
- }
- if (fireAlarm) {
- informAnomalyAlarmFiredLocked(NanoToMillis(elapsedRealtimeNs));
- }
-
- int64_t curTimeSec = getElapsedRealtimeSec();
- if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
- mPullerManager->ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
- mLastPullerCacheClearTimeSec = curTimeSec;
- }
-
- std::unordered_set<int> uidsWithActiveConfigsChanged;
- std::unordered_map<int, std::vector<int64_t>> activeConfigsPerUid;
- // pass the event to metrics managers.
- for (auto& pair : mMetricsManagers) {
- int uid = pair.first.GetUid();
- int64_t configId = pair.first.GetId();
- bool isPrevActive = pair.second->isActive();
- pair.second->onLogEvent(*event);
- bool isCurActive = pair.second->isActive();
- // Map all active configs by uid.
- if (isCurActive) {
- auto activeConfigs = activeConfigsPerUid.find(uid);
- if (activeConfigs != activeConfigsPerUid.end()) {
- activeConfigs->second.push_back(configId);
- } else {
- vector<int64_t> newActiveConfigs;
- newActiveConfigs.push_back(configId);
- activeConfigsPerUid[uid] = newActiveConfigs;
- }
- }
- // The activation state of this config changed.
- if (isPrevActive != isCurActive) {
- VLOG("Active status changed for uid %d", uid);
- uidsWithActiveConfigsChanged.insert(uid);
- StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive);
- }
- flushIfNecessaryLocked(pair.first, *(pair.second));
- }
-
- // Don't use the event timestamp for the guardrail.
- for (int uid : uidsWithActiveConfigsChanged) {
- // Send broadcast so that receivers can pull data.
- auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid);
- if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) {
- if (elapsedRealtimeNs - lastBroadcastTime->second <
- StatsdStats::kMinActivationBroadcastPeriodNs) {
- StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid);
- VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us.");
- return;
- }
- }
- auto activeConfigs = activeConfigsPerUid.find(uid);
- if (activeConfigs != activeConfigsPerUid.end()) {
- if (mSendActivationBroadcast(uid, activeConfigs->second)) {
- VLOG("StatsD sent activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
- }
- } else {
- std::vector<int64_t> emptyActiveConfigs;
- if (mSendActivationBroadcast(uid, emptyActiveConfigs)) {
- VLOG("StatsD sent EMPTY activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
- }
- }
- }
-}
-
-void StatsLogProcessor::GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- GetActiveConfigsLocked(uid, outActiveConfigs);
-}
-
-void StatsLogProcessor::GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs) {
- outActiveConfigs.clear();
- for (auto& pair : mMetricsManagers) {
- if (pair.first.GetUid() == uid && pair.second->isActive()) {
- outActiveConfigs.push_back(pair.first.GetId());
- }
- }
-}
-
-void StatsLogProcessor::OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
- const StatsdConfig& config, bool modularUpdate) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- WriteDataToDiskLocked(key, timestampNs, CONFIG_UPDATED, NO_TIME_CONSTRAINTS);
- OnConfigUpdatedLocked(timestampNs, key, config, modularUpdate);
-}
-
-void StatsLogProcessor::OnConfigUpdatedLocked(const int64_t timestampNs, const ConfigKey& key,
- const StatsdConfig& config, bool modularUpdate) {
- VLOG("Updated configuration for key %s", key.ToString().c_str());
- // Create new config if this is not a modular update or if this is a new config.
- const auto& it = mMetricsManagers.find(key);
- bool configValid = false;
- if (!modularUpdate || it == mMetricsManagers.end()) {
- sp<MetricsManager> newMetricsManager =
- new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
- mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
- configValid = newMetricsManager->isConfigValid();
- if (configValid) {
- newMetricsManager->init();
- mUidMap->OnConfigUpdated(key);
- newMetricsManager->refreshTtl(timestampNs);
- mMetricsManagers[key] = newMetricsManager;
- VLOG("StatsdConfig valid");
- }
- } else {
- // Preserve the existing MetricsManager, update necessary components and metadata in place.
- configValid = it->second->updateConfig(config, mTimeBaseNs, timestampNs,
- mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
- if (configValid) {
- mUidMap->OnConfigUpdated(key);
- }
- }
- if (!configValid) {
- // If there is any error in the config, don't use it.
- // Remove any existing config with the same key.
- ALOGE("StatsdConfig NOT valid");
- mMetricsManagers.erase(key);
- }
-}
-
-size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- auto it = mMetricsManagers.find(key);
- if (it == mMetricsManagers.end()) {
- ALOGW("Config source %s does not exist", key.ToString().c_str());
- return 0;
- }
- return it->second->byteSize();
-}
-
-void StatsLogProcessor::dumpStates(int out, bool verbose) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- FILE* fout = fdopen(out, "w");
- if (fout == NULL) {
- return;
- }
- fprintf(fout, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
- for (auto metricsManager : mMetricsManagers) {
- metricsManager.second->dumpStates(fout, verbose);
- }
-
- fclose(fout);
-}
-
-/*
- * onDumpReport dumps serialized ConfigMetricsReportList into proto.
- */
-void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency,
- ProtoOutputStream* proto) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
-
- // Start of ConfigKey.
- uint64_t configKeyToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
- proto->end(configKeyToken);
- // End of ConfigKey.
-
- bool keepFile = false;
- auto it = mMetricsManagers.find(key);
- if (it != mMetricsManagers.end() && it->second->shouldPersistLocalHistory()) {
- keepFile = true;
- }
-
- // Then, check stats-data directory to see there's any file containing
- // ConfigMetricsReport from previous shutdowns to concatenate to reports.
- StorageManager::appendConfigMetricsReport(
- key, proto, erase_data && !keepFile /* should remove file after appending it */,
- dumpReportReason == ADB_DUMP /*if caller is adb*/);
-
- if (it != mMetricsManagers.end()) {
- // This allows another broadcast to be sent within the rate-limit period if we get close to
- // filling the buffer again soon.
- mLastBroadcastTimes.erase(key);
-
- vector<uint8_t> buffer;
- onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket,
- erase_data, dumpReportReason, dumpLatency,
- false /* is this data going to be saved on disk */, &buffer);
- proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
- reinterpret_cast<char*>(buffer.data()), buffer.size());
- } else {
- ALOGW("Config source %s does not exist", key.ToString().c_str());
- }
-}
-
-/*
- * onDumpReport dumps serialized ConfigMetricsReportList into outData.
- */
-void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency,
- vector<uint8_t>* outData) {
- ProtoOutputStream proto;
- onDumpReport(key, dumpTimeStampNs, include_current_partial_bucket, erase_data,
- dumpReportReason, dumpLatency, &proto);
-
- if (outData != nullptr) {
- flushProtoToBuffer(proto, outData);
- VLOG("output data size %zu", outData->size());
- }
-
- StatsdStats::getInstance().noteMetricsReportSent(key, proto.size());
-}
-
-/*
- * onConfigMetricsReportLocked dumps serialized ConfigMetricsReport into outData.
- */
-void StatsLogProcessor::onConfigMetricsReportLocked(
- const ConfigKey& key, const int64_t dumpTimeStampNs,
- const bool include_current_partial_bucket, const bool erase_data,
- const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
- const bool dataSavedOnDisk, vector<uint8_t>* buffer) {
- // We already checked whether key exists in mMetricsManagers in
- // WriteDataToDisk.
- auto it = mMetricsManagers.find(key);
- if (it == mMetricsManagers.end()) {
- return;
- }
- int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
- int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
-
- std::set<string> str_set;
-
- ProtoOutputStream tempProto;
- // First, fill in ConfigMetricsReport using current data on memory, which
- // starts from filling in StatsLogReport's.
- it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
- dumpLatency, &str_set, &tempProto);
-
- // Fill in UidMap if there is at least one metric to report.
- // This skips the uid map if it's an empty config.
- if (it->second->getNumMetrics() > 0) {
- uint64_t uidMapToken = tempProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
- mUidMap->appendUidMap(
- dumpTimeStampNs, key, it->second->hashStringInReport() ? &str_set : nullptr,
- it->second->versionStringsInReport(), it->second->installerInReport(), &tempProto);
- tempProto.end(uidMapToken);
- }
-
- // Fill in the timestamps.
- tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_ELAPSED_NANOS,
- (long long)lastReportTimeNs);
- tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_ELAPSED_NANOS,
- (long long)dumpTimeStampNs);
- tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_WALL_CLOCK_NANOS,
- (long long)lastReportWallClockNs);
- tempProto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
- (long long)getWallClockNs());
- // Dump report reason
- tempProto.write(FIELD_TYPE_INT32 | FIELD_ID_DUMP_REPORT_REASON, dumpReportReason);
-
- for (const auto& str : str_set) {
- tempProto.write(FIELD_TYPE_STRING | FIELD_COUNT_REPEATED | FIELD_ID_STRINGS, str);
- }
-
- flushProtoToBuffer(tempProto, buffer);
-
- // save buffer to disk if needed
- if (erase_data && !dataSavedOnDisk && it->second->shouldPersistLocalHistory()) {
- VLOG("save history to disk");
- string file_name = StorageManager::getDataHistoryFileName((long)getWallClockSec(),
- key.GetUid(), key.GetId());
- StorageManager::writeFile(file_name.c_str(), buffer->data(), buffer->size());
- }
-}
-
-void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs,
- const std::vector<ConfigKey>& configs) {
- for (const auto& key : configs) {
- StatsdConfig config;
- if (StorageManager::readConfigFromDisk(key, &config)) {
- // Force a full update when resetting a config.
- OnConfigUpdatedLocked(timestampNs, key, config, /*modularUpdate=*/false);
- StatsdStats::getInstance().noteConfigReset(key);
- } else {
- ALOGE("Failed to read backup config from disk for : %s", key.ToString().c_str());
- auto it = mMetricsManagers.find(key);
- if (it != mMetricsManagers.end()) {
- it->second->refreshTtl(timestampNs);
- }
- }
- }
-}
-
-void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) {
- std::vector<ConfigKey> configKeysTtlExpired;
- for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
- if (it->second != nullptr && !it->second->isInTtl(timestampNs)) {
- configKeysTtlExpired.push_back(it->first);
- }
- }
- if (configKeysTtlExpired.size() > 0) {
- WriteDataToDiskLocked(CONFIG_RESET, NO_TIME_CONSTRAINTS);
- resetConfigsLocked(timestampNs, configKeysTtlExpired);
- }
-}
-
-void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- auto it = mMetricsManagers.find(key);
- if (it != mMetricsManagers.end()) {
- WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED,
- NO_TIME_CONSTRAINTS);
- mMetricsManagers.erase(it);
- mUidMap->OnConfigRemoved(key);
- }
- StatsdStats::getInstance().noteConfigRemoved(key);
-
- mLastBroadcastTimes.erase(key);
-
- int uid = key.GetUid();
- bool lastConfigForUid = true;
- for (auto it : mMetricsManagers) {
- if (it.first.GetUid() == uid) {
- lastConfigForUid = false;
- break;
- }
- }
- if (lastConfigForUid) {
- mLastActivationBroadcastTimes.erase(uid);
- }
-
- if (mMetricsManagers.empty()) {
- mPullerManager->ForceClearPullerCache();
- }
-}
-
-void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key,
- MetricsManager& metricsManager) {
- int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
- auto lastCheckTime = mLastByteSizeTimes.find(key);
- if (lastCheckTime != mLastByteSizeTimes.end()) {
- if (elapsedRealtimeNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
- return;
- }
- }
-
- // We suspect that the byteSize() computation is expensive, so we set a rate limit.
- size_t totalBytes = metricsManager.byteSize();
- mLastByteSizeTimes[key] = elapsedRealtimeNs;
- bool requestDump = false;
- if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) {
- // Too late. We need to start clearing data.
- metricsManager.dropData(elapsedRealtimeNs);
- StatsdStats::getInstance().noteDataDropped(key, totalBytes);
- VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
- } else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
- (mOnDiskDataConfigs.find(key) != mOnDiskDataConfigs.end())) {
- // Request to send a broadcast if:
- // 1. in memory data > threshold OR
- // 2. config has old data report on disk.
- requestDump = true;
- }
-
- if (requestDump) {
- // Send broadcast so that receivers can pull data.
- auto lastBroadcastTime = mLastBroadcastTimes.find(key);
- if (lastBroadcastTime != mLastBroadcastTimes.end()) {
- if (elapsedRealtimeNs - lastBroadcastTime->second <
- StatsdStats::kMinBroadcastPeriodNs) {
- VLOG("StatsD would've sent a broadcast but the rate limit stopped us.");
- return;
- }
- }
- if (mSendBroadcast(key)) {
- mOnDiskDataConfigs.erase(key);
- VLOG("StatsD triggered data fetch for %s", key.ToString().c_str());
- mLastBroadcastTimes[key] = elapsedRealtimeNs;
- StatsdStats::getInstance().noteBroadcastSent(key);
- }
- }
-}
-
-void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key,
- const int64_t timestampNs,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency) {
- if (mMetricsManagers.find(key) == mMetricsManagers.end() ||
- !mMetricsManagers.find(key)->second->shouldWriteToDisk()) {
- return;
- }
- vector<uint8_t> buffer;
- onConfigMetricsReportLocked(key, timestampNs, true /* include_current_partial_bucket*/,
- true /* erase_data */, dumpReportReason, dumpLatency, true,
- &buffer);
- string file_name =
- StorageManager::getDataFileName((long)getWallClockSec(), key.GetUid(), key.GetId());
- StorageManager::writeFile(file_name.c_str(), buffer.data(), buffer.size());
-
- // We were able to write the ConfigMetricsReport to disk, so we should trigger collection ASAP.
- mOnDiskDataConfigs.insert(key);
-}
-
-void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- const int64_t timeNs = getElapsedRealtimeNs();
- // Do not write to disk if we already have in the last few seconds.
- if (static_cast<unsigned long long> (timeNs) <
- mLastActiveMetricsWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
- ALOGI("Statsd skipping writing active metrics to disk. Already wrote data in last %d seconds",
- WRITE_DATA_COOL_DOWN_SEC);
- return;
- }
- mLastActiveMetricsWriteNs = timeNs;
-
- ProtoOutputStream proto;
- WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, DEVICE_SHUTDOWN, &proto);
-
- string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
- StorageManager::deleteFile(file_name.c_str());
- android::base::unique_fd fd(
- open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
- if (fd == -1) {
- ALOGE("Attempt to write %s but failed", file_name.c_str());
- return;
- }
- proto.flush(fd.get());
-}
-
-void StatsLogProcessor::SaveMetadataToDisk(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- // Do not write to disk if we already have in the last few seconds.
- if (static_cast<unsigned long long> (systemElapsedTimeNs) <
- mLastMetadataWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
- ALOGI("Statsd skipping writing metadata to disk. Already wrote data in last %d seconds",
- WRITE_DATA_COOL_DOWN_SEC);
- return;
- }
- mLastMetadataWriteNs = systemElapsedTimeNs;
-
- metadata::StatsMetadataList metadataList;
- WriteMetadataToProtoLocked(
- currentWallClockTimeNs, systemElapsedTimeNs, &metadataList);
-
- string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR);
- StorageManager::deleteFile(file_name.c_str());
-
- if (metadataList.stats_metadata_size() == 0) {
- // Skip the write if we have nothing to write.
- return;
- }
-
- std::string data;
- metadataList.SerializeToString(&data);
- StorageManager::writeFile(file_name.c_str(), data.c_str(), data.size());
-}
-
-void StatsLogProcessor::WriteMetadataToProto(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::StatsMetadataList* metadataList) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- WriteMetadataToProtoLocked(currentWallClockTimeNs, systemElapsedTimeNs, metadataList);
-}
-
-void StatsLogProcessor::WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::StatsMetadataList* metadataList) {
- for (const auto& pair : mMetricsManagers) {
- const sp<MetricsManager>& metricsManager = pair.second;
- metadata::StatsMetadata* statsMetadata = metadataList->add_stats_metadata();
- bool metadataWritten = metricsManager->writeMetadataToProto(currentWallClockTimeNs,
- systemElapsedTimeNs, statsMetadata);
- if (!metadataWritten) {
- metadataList->mutable_stats_metadata()->RemoveLast();
- }
- }
-}
-
-void StatsLogProcessor::LoadMetadataFromDisk(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR);
- int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
- if (-1 == fd) {
- VLOG("Attempt to read %s but failed", file_name.c_str());
- StorageManager::deleteFile(file_name.c_str());
- return;
- }
- string content;
- if (!android::base::ReadFdToString(fd, &content)) {
- ALOGE("Attempt to read %s but failed", file_name.c_str());
- close(fd);
- StorageManager::deleteFile(file_name.c_str());
- return;
- }
-
- close(fd);
-
- metadata::StatsMetadataList statsMetadataList;
- if (!statsMetadataList.ParseFromString(content)) {
- ALOGE("Attempt to read %s but failed; failed to metadata", file_name.c_str());
- StorageManager::deleteFile(file_name.c_str());
- return;
- }
- SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs);
- StorageManager::deleteFile(file_name.c_str());
-}
-
-void StatsLogProcessor::SetMetadataState(const metadata::StatsMetadataList& statsMetadataList,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs);
-}
-
-void StatsLogProcessor::SetMetadataStateLocked(
- const metadata::StatsMetadataList& statsMetadataList,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs) {
- for (const metadata::StatsMetadata& metadata : statsMetadataList.stats_metadata()) {
- ConfigKey key(metadata.config_key().uid(), metadata.config_key().config_id());
- auto it = mMetricsManagers.find(key);
- if (it == mMetricsManagers.end()) {
- ALOGE("No config found for configKey %s", key.ToString().c_str());
- continue;
- }
- VLOG("Setting metadata %s", key.ToString().c_str());
- it->second->loadMetadata(metadata, currentWallClockTimeNs, systemElapsedTimeNs);
- }
- VLOG("Successfully loaded %d metadata.", statsMetadataList.stats_metadata_size());
-}
-
-void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- WriteActiveConfigsToProtoOutputStreamLocked(currentTimeNs, reason, proto);
-}
-
-void StatsLogProcessor::WriteActiveConfigsToProtoOutputStreamLocked(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
- for (const auto& pair : mMetricsManagers) {
- const sp<MetricsManager>& metricsManager = pair.second;
- uint64_t configToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG);
- metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, reason, proto);
- proto->end(configToken);
- }
-}
-void StatsLogProcessor::LoadActiveConfigsFromDisk() {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR);
- int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
- if (-1 == fd) {
- VLOG("Attempt to read %s but failed", file_name.c_str());
- StorageManager::deleteFile(file_name.c_str());
- return;
- }
- string content;
- if (!android::base::ReadFdToString(fd, &content)) {
- ALOGE("Attempt to read %s but failed", file_name.c_str());
- close(fd);
- StorageManager::deleteFile(file_name.c_str());
- return;
- }
-
- close(fd);
-
- ActiveConfigList activeConfigList;
- if (!activeConfigList.ParseFromString(content)) {
- ALOGE("Attempt to read %s but failed; failed to load active configs", file_name.c_str());
- StorageManager::deleteFile(file_name.c_str());
- return;
- }
- // Passing in mTimeBaseNs only works as long as we only load from disk is when statsd starts.
- SetConfigsActiveStateLocked(activeConfigList, mTimeBaseNs);
- StorageManager::deleteFile(file_name.c_str());
-}
-
-void StatsLogProcessor::SetConfigsActiveState(const ActiveConfigList& activeConfigList,
- int64_t currentTimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- SetConfigsActiveStateLocked(activeConfigList, currentTimeNs);
-}
-
-void StatsLogProcessor::SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
- int64_t currentTimeNs) {
- for (int i = 0; i < activeConfigList.config_size(); i++) {
- const auto& config = activeConfigList.config(i);
- ConfigKey key(config.uid(), config.id());
- auto it = mMetricsManagers.find(key);
- if (it == mMetricsManagers.end()) {
- ALOGE("No config found for config %s", key.ToString().c_str());
- continue;
- }
- VLOG("Setting active config %s", key.ToString().c_str());
- it->second->loadActiveConfig(config, currentTimeNs);
- }
- VLOG("Successfully loaded %d active configs.", activeConfigList.config_size());
-}
-
-void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency) {
- const int64_t timeNs = getElapsedRealtimeNs();
- // Do not write to disk if we already have in the last few seconds.
- // This is to avoid overwriting files that would have the same name if we
- // write twice in the same second.
- if (static_cast<unsigned long long> (timeNs) <
- mLastWriteTimeNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
- ALOGI("Statsd skipping writing data to disk. Already wrote data in last %d seconds",
- WRITE_DATA_COOL_DOWN_SEC);
- return;
- }
- mLastWriteTimeNs = timeNs;
- for (auto& pair : mMetricsManagers) {
- WriteDataToDiskLocked(pair.first, timeNs, dumpReportReason, dumpLatency);
- }
-}
-
-void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- WriteDataToDiskLocked(dumpReportReason, dumpLatency);
-}
-
-void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- mPullerManager->OnAlarmFired(timestampNs);
-}
-
-int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) {
- auto it = mMetricsManagers.find(key);
- if (it == mMetricsManagers.end()) {
- return 0;
- } else {
- return it->second->getLastReportTimeNs();
- }
-}
-
-void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
- const int uid, const int64_t version) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- VLOG("Received app upgrade");
- StateManager::getInstance().notifyAppChanged(apk, mUidMap);
- for (const auto& it : mMetricsManagers) {
- it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version);
- }
-}
-
-void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
- const int uid) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- VLOG("Received app removed");
- StateManager::getInstance().notifyAppChanged(apk, mUidMap);
- for (const auto& it : mMetricsManagers) {
- it.second->notifyAppRemoved(eventTimeNs, apk, uid);
- }
-}
-
-void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- VLOG("Received uid map");
- StateManager::getInstance().updateLogSources(mUidMap);
- for (const auto& it : mMetricsManagers) {
- it.second->onUidMapReceived(eventTimeNs);
- }
-}
-
-void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- VLOG("Received boot completed signal");
- for (const auto& it : mMetricsManagers) {
- it.second->onStatsdInitCompleted(elapsedTimeNs);
- }
-}
-
-void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- mOnDiskDataConfigs.insert(key);
-}
-
-void StatsLogProcessor::setAnomalyAlarm(const int64_t elapsedTimeMillis) {
- std::lock_guard<std::mutex> lock(mAnomalyAlarmMutex);
- mNextAnomalyAlarmTime = elapsedTimeMillis;
-}
-
-void StatsLogProcessor::cancelAnomalyAlarm() {
- std::lock_guard<std::mutex> lock(mAnomalyAlarmMutex);
- mNextAnomalyAlarmTime = 0;
-}
-
-void StatsLogProcessor::informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis) {
- VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called");
- std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
- mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(elapsedTimeMillis / 1000));
- if (alarmSet.size() > 0) {
- VLOG("Found periodic alarm fired.");
- processFiredAnomalyAlarmsLocked(MillisToNano(elapsedTimeMillis), alarmSet);
- } else {
- ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled.");
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
deleted file mode 100644
index 2af277ad1e5b..000000000000
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ /dev/null
@@ -1,374 +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.
- */
-
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include "config/ConfigListener.h"
-#include "logd/LogEvent.h"
-#include "metrics/MetricsManager.h"
-#include "packages/UidMap.h"
-#include "external/StatsPullerManager.h"
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
-
-#include <stdio.h>
-#include <unordered_map>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-
-class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener {
-public:
- StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
- const int64_t timeBaseNs,
- const std::function<bool(const ConfigKey&)>& sendBroadcast,
- const std::function<bool(const int&,
- const vector<int64_t>&)>& sendActivationBroadcast);
- virtual ~StatsLogProcessor();
-
- void OnLogEvent(LogEvent* event);
-
- void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
- const StatsdConfig& config, bool modularUpdate = false);
- void OnConfigRemoved(const ConfigKey& key);
-
- size_t GetMetricsSize(const ConfigKey& key) const;
-
- void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs);
-
- void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
- const bool include_current_partial_bucket, const bool erase_data,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency,
- vector<uint8_t>* outData);
- void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
- const bool include_current_partial_bucket, const bool erase_data,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency,
- ProtoOutputStream* proto);
-
- /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */
- void onPeriodicAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
-
- /* Flushes data to disk. Data on memory will be gone after written to disk. */
- void WriteDataToDisk(const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency);
-
- /* Persist configs containing metrics with active activations to disk. */
- void SaveActiveConfigsToDisk(int64_t currentTimeNs);
-
- /* Writes the current active status/ttl for all configs and metrics to ProtoOutputStream. */
- void WriteActiveConfigsToProtoOutputStream(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
-
- /* Load configs containing metrics with active activations from disk. */
- void LoadActiveConfigsFromDisk();
-
- /* Persist metadata for configs and metrics to disk. */
- void SaveMetadataToDisk(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs);
-
- /* Writes the statsd metadata for all configs and metrics to StatsMetadataList. */
- void WriteMetadataToProto(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::StatsMetadataList* metadataList);
-
- /* Load stats metadata for configs and metrics from disk. */
- void LoadMetadataFromDisk(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs);
-
- /* Sets the metadata for all configs and metrics */
- void SetMetadataState(const metadata::StatsMetadataList& statsMetadataList,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs);
-
- /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
- void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
-
- /* Notify all MetricsManagers of app upgrades */
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override;
-
- /* Notify all MetricsManagers of app removals */
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
-
- /* Notify all MetricsManagers of uid map snapshots received */
- void onUidMapReceived(const int64_t& eventTimeNs) override;
-
- /* Notify all metrics managers of boot completed
- * This will force a bucket split when the boot is finished.
- */
- void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
-
- // Reset all configs.
- void resetConfigs();
-
- inline sp<UidMap> getUidMap() {
- return mUidMap;
- }
-
- void dumpStates(int outFd, bool verbose);
-
- void informPullAlarmFired(const int64_t timestampNs);
-
- int64_t getLastReportTimeNs(const ConfigKey& key);
-
- inline void setPrintLogs(bool enabled) {
- std::lock_guard<std::mutex> lock(mMetricsMutex);
- mPrintAllLogs = enabled;
- }
-
- // Add a specific config key to the possible configs to dump ASAP.
- void noteOnDiskData(const ConfigKey& key);
-
- void setAnomalyAlarm(const int64_t timeMillis);
-
- void cancelAnomalyAlarm();
-
-private:
- // For testing only.
- inline sp<AlarmMonitor> getAnomalyAlarmMonitor() const {
- return mAnomalyAlarmMonitor;
- }
-
- inline sp<AlarmMonitor> getPeriodicAlarmMonitor() const {
- return mPeriodicAlarmMonitor;
- }
-
- mutable mutex mMetricsMutex;
-
- // Guards mNextAnomalyAlarmTime. A separate mutex is needed because alarms are set/cancelled
- // in the onLogEvent code path, which is locked by mMetricsMutex.
- // DO NOT acquire mMetricsMutex while holding mAnomalyAlarmMutex. This can lead to a deadlock.
- mutable mutex mAnomalyAlarmMutex;
-
- std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers;
-
- std::unordered_map<ConfigKey, int64_t> mLastBroadcastTimes;
-
- // Last time we sent a broadcast to this uid that the active configs had changed.
- std::unordered_map<int, int64_t> mLastActivationBroadcastTimes;
-
- // Tracks when we last checked the bytes consumed for each config key.
- std::unordered_map<ConfigKey, int64_t> mLastByteSizeTimes;
-
- // Tracks which config keys has metric reports on disk
- std::set<ConfigKey> mOnDiskDataConfigs;
-
- sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid.
-
- sp<StatsPullerManager> mPullerManager; // Reference to StatsPullerManager
-
- sp<AlarmMonitor> mAnomalyAlarmMonitor;
-
- sp<AlarmMonitor> mPeriodicAlarmMonitor;
-
- void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs);
-
- void resetIfConfigTtlExpiredLocked(const int64_t timestampNs);
-
- void OnConfigUpdatedLocked(const int64_t currentTimestampNs, const ConfigKey& key,
- const StatsdConfig& config, bool modularUpdate);
-
- void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
-
- void WriteActiveConfigsToProtoOutputStreamLocked(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
-
- void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
- int64_t currentTimeNs);
-
- void SetMetadataStateLocked(const metadata::StatsMetadataList& statsMetadataList,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs);
-
- void WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::StatsMetadataList* metadataList);
-
- void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency);
-
- void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
- const DumpReportReason dumpReportReason,
- const DumpLatency dumpLatency);
-
- void onConfigMetricsReportLocked(
- const ConfigKey& key, const int64_t dumpTimeStampNs,
- const bool include_current_partial_bucket, const bool erase_data,
- const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
- /*if dataSavedToDisk is true, it indicates the caller will write the data to disk
- (e.g., before reboot). So no need to further persist local history.*/
- const bool dataSavedToDisk, vector<uint8_t>* proto);
-
- /* Check if we should send a broadcast if approaching memory limits and if we're over, we
- * actually delete the data. */
- void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager);
-
- // Maps the isolated uid in the log event to host uid if the log event contains uid fields.
- void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const;
-
- // Handler over the isolated uid change event.
- void onIsolatedUidChangedEventLocked(const LogEvent& event);
-
- // Handler over the binary push state changed event.
- void onBinaryPushStateChangedEventLocked(LogEvent* event);
-
- // Handler over the watchdog rollback occurred event.
- void onWatchdogRollbackOccurredLocked(LogEvent* event);
-
- // Updates train info on disk based on binary push state changed info and
- // write disk info into parameters.
- void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn);
-
- // Gets experiment ids on disk for associated train and updates them
- // depending on rollback type. Then writes them back to disk and returns
- // them.
- std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
- const string& packageName);
-
- // Reset all configs.
- void resetConfigsLocked(const int64_t timestampNs);
- // Reset the specified configs.
- void resetConfigsLocked(const int64_t timestampNs, const std::vector<ConfigKey>& configs);
-
- // An anomaly alarm should have fired.
- // Check with anomaly alarm manager to find the alarms and process the result.
- void informAnomalyAlarmFiredLocked(const int64_t elapsedTimeMillis);
-
- /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
- void processFiredAnomalyAlarmsLocked(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet);
-
- // Function used to send a broadcast so that receiver for the config key can call getData
- // to retrieve the stored data.
- std::function<bool(const ConfigKey& key)> mSendBroadcast;
-
- // Function used to send a broadcast so that receiver can be notified of which configs
- // are currently active.
- std::function<bool(const int& uid, const vector<int64_t>& configIds)> mSendActivationBroadcast;
-
- const int64_t mTimeBaseNs;
-
- // Largest timestamp of the events that we have processed.
- int64_t mLargestTimestampSeen = 0;
-
- int64_t mLastTimestampSeen = 0;
-
- int64_t mLastPullerCacheClearTimeSec = 0;
-
- // Last time we wrote data to disk.
- int64_t mLastWriteTimeNs = 0;
-
- // Last time we wrote active metrics to disk.
- int64_t mLastActiveMetricsWriteNs = 0;
-
- //Last time we wrote metadata to disk.
- int64_t mLastMetadataWriteNs = 0;
-
- // The time for the next anomaly alarm for alerts.
- int64_t mNextAnomalyAlarmTime = 0;
-
- bool mPrintAllLogs = false;
-
- FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
- FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
- FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
- FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
- FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
- FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
- FRIEND_TEST(StatsLogProcessorTest,
- TestActivationOnBootMultipleActivationsDifferentActivationTypes);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
-
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3);
- FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1);
- FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2);
- FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
- FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
- FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
- FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
-
- FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
-
- FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
- FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
-
- FRIEND_TEST(ConfigUpdateE2eTest, TestHashStrings);
- FRIEND_TEST(ConfigUpdateE2eTest, TestUidMapVersionStringInstaller);
- FRIEND_TEST(ConfigUpdateE2eTest, TestConfigTtl);
-
- FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
- FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
-
- FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
- FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
- FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
- FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
- FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
- FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
-
- FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
deleted file mode 100644
index 68c2dd56ef13..000000000000
--- a/cmds/statsd/src/StatsService.cpp
+++ /dev/null
@@ -1,1312 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StatsService.h"
-#include "stats_log_util.h"
-#include "android-base/stringprintf.h"
-#include "config/ConfigKey.h"
-#include "config/ConfigManager.h"
-#include "guardrail/StatsdStats.h"
-#include "storage/StorageManager.h"
-#include "subscriber/SubscriberReporter.h"
-
-#include <android-base/file.h>
-#include <android-base/strings.h>
-#include <cutils/multiuser.h>
-#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
-#include <frameworks/base/cmds/statsd/src/uid_data.pb.h>
-#include <private/android_filesystem_config.h>
-#include <statslog_statsd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/system_properties.h>
-#include <unistd.h>
-#include <utils/String16.h>
-
-using namespace android;
-
-using android::base::StringPrintf;
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_MESSAGE;
-
-using Status = ::ndk::ScopedAStatus;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-constexpr const char* kPermissionDump = "android.permission.DUMP";
-
-constexpr const char* kPermissionRegisterPullAtom = "android.permission.REGISTER_STATS_PULL_ATOM";
-
-#define STATS_SERVICE_DIR "/data/misc/stats-service"
-
-// for StatsDataDumpProto
-const int FIELD_ID_REPORTS_LIST = 1;
-
-static Status exception(int32_t code, const std::string& msg) {
- ALOGE("%s (%d)", msg.c_str(), code);
- return Status::fromExceptionCodeWithMessage(code, msg.c_str());
-}
-
-static bool checkPermission(const char* permission) {
- pid_t pid = AIBinder_getCallingPid();
- uid_t uid = AIBinder_getCallingUid();
- return checkPermissionForIds(permission, pid, uid);
-}
-
-Status checkUid(uid_t expectedUid) {
- uid_t uid = AIBinder_getCallingUid();
- if (uid == expectedUid || uid == AID_ROOT) {
- return Status::ok();
- } else {
- return exception(EX_SECURITY,
- StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
- }
-}
-
-#define ENFORCE_UID(uid) { \
- Status status = checkUid((uid)); \
- if (!status.isOk()) { \
- return status; \
- } \
-}
-
-StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
- : mAnomalyAlarmMonitor(new AlarmMonitor(
- MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [this](const shared_ptr<IStatsCompanionService>& /*sc*/, int64_t timeMillis) {
- mProcessor->setAnomalyAlarm(timeMillis);
- StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
- },
- [this](const shared_ptr<IStatsCompanionService>& /*sc*/) {
- mProcessor->cancelAnomalyAlarm();
- StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
- })),
- mPeriodicAlarmMonitor(new AlarmMonitor(
- MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
- if (sc != nullptr) {
- sc->setAlarmForSubscriberTriggering(timeMillis);
- StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
- }
- },
- [](const shared_ptr<IStatsCompanionService>& sc) {
- if (sc != nullptr) {
- sc->cancelAlarmForSubscriberTriggering();
- StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
- }
- })),
- mEventQueue(queue),
- mBootCompleteTrigger({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag},
- [this]() { mProcessor->onStatsdInitCompleted(getElapsedRealtimeNs()); }),
- mStatsCompanionServiceDeathRecipient(
- AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) {
- mUidMap = UidMap::getInstance();
- mPullerManager = new StatsPullerManager();
- StatsPuller::SetUidMap(mUidMap);
- mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(
- mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
- getElapsedRealtimeNs(),
- [this](const ConfigKey& key) {
- shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
- if (receiver == nullptr) {
- VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str());
- return false;
- } else if (receiver->sendDataBroadcast(
- mProcessor->getLastReportTimeNs(key)).isOk()) {
- return true;
- } else {
- VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str());
- return false;
- }
- },
- [this](const int& uid, const vector<int64_t>& activeConfigs) {
- shared_ptr<IPendingIntentRef> receiver =
- mConfigManager->GetActiveConfigsChangedReceiver(uid);
- if (receiver == nullptr) {
- VLOG("Could not find receiver for uid %d", uid);
- return false;
- } else if (receiver->sendActiveConfigsChangedBroadcast(activeConfigs).isOk()) {
- VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid);
- return true;
- } else {
- VLOG("StatsService::active configs broadcast failed for uid %d" , uid);
- return false;
- }
- });
-
- mUidMap->setListener(mProcessor);
- mConfigManager->AddListener(mProcessor);
-
- init_system_properties();
-
- if (mEventQueue != nullptr) {
- std::thread pushedEventThread([this] { readLogs(); });
- pushedEventThread.detach();
- }
-}
-
-StatsService::~StatsService() {
-}
-
-/* Runs on a dedicated thread to process pushed events. */
-void StatsService::readLogs() {
- // Read forever..... long live statsd
- while (1) {
- // Block until an event is available.
- auto event = mEventQueue->waitPop();
- // Pass it to StatsLogProcess to all configs/metrics
- // At this point, the LogEventQueue is not blocked, so that the socketListener
- // can read events from the socket and write to buffer to avoid data drop.
- mProcessor->OnLogEvent(event.get());
- // The ShellSubscriber is only used by shell for local debugging.
- if (mShellSubscriber != nullptr) {
- mShellSubscriber->onLogEvent(*event);
- }
- }
-}
-
-void StatsService::init_system_properties() {
- mEngBuild = false;
- const prop_info* buildType = __system_property_find("ro.build.type");
- if (buildType != NULL) {
- __system_property_read_callback(buildType, init_build_type_callback, this);
- }
-}
-
-void StatsService::init_build_type_callback(void* cookie, const char* /*name*/, const char* value,
- uint32_t serial) {
- if (0 == strcmp("eng", value) || 0 == strcmp("userdebug", value)) {
- reinterpret_cast<StatsService*>(cookie)->mEngBuild = true;
- }
-}
-
-/**
- * Write data from statsd.
- * Format for statsdStats: adb shell dumpsys stats --metadata [-v] [--proto]
- * Format for data report: adb shell dumpsys stats [anything other than --metadata] [--proto]
- * Anything ending in --proto will be in proto format.
- * Anything without --metadata as the first argument will be report information.
- * (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
- * TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
- */
-status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) {
- if (!checkPermission(kPermissionDump)) {
- return PERMISSION_DENIED;
- }
-
- int lastArg = numArgs - 1;
- bool asProto = false;
- if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument
- asProto = true;
- lastArg--;
- }
- if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument
- // Request is to dump statsd stats.
- bool verbose = false;
- if (lastArg >= 0 && string(args[lastArg]) == "-v") {
- verbose = true;
- lastArg--;
- }
- dumpStatsdStats(fd, verbose, asProto);
- } else {
- // Request is to dump statsd report data.
- if (asProto) {
- dumpIncidentSection(fd);
- } else {
- dprintf(fd, "Non-proto format of stats data dump not available; see proto version.\n");
- }
- }
-
- return NO_ERROR;
-}
-
-/**
- * Write debugging data about statsd in text or proto format.
- */
-void StatsService::dumpStatsdStats(int out, bool verbose, bool proto) {
- if (proto) {
- vector<uint8_t> data;
- StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats.
- for (size_t i = 0; i < data.size(); i ++) {
- dprintf(out, "%c", data[i]);
- }
- } else {
- StatsdStats::getInstance().dumpStats(out);
- mProcessor->dumpStates(out, verbose);
- }
-}
-
-/**
- * Write stats report data in StatsDataDumpProto incident section format.
- */
-void StatsService::dumpIncidentSection(int out) {
- ProtoOutputStream proto;
- for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) {
- uint64_t reportsListToken =
- proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST);
- // Don't include the current bucket to avoid skipping buckets.
- // If we need to include the current bucket later, consider changing to NO_TIME_CONSTRAINTS
- // or other alternatives to avoid skipping buckets for pulled metrics.
- mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
- false /* includeCurrentBucket */, false /* erase_data */,
- ADB_DUMP,
- FAST,
- &proto);
- proto.end(reportsListToken);
- proto.flush(out);
- proto.clear();
- }
-}
-
-/**
- * Implementation of the adb shell cmd stats command.
- */
-status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv,
- uint32_t argc) {
- uid_t uid = AIBinder_getCallingUid();
- if (uid != AID_ROOT && uid != AID_SHELL) {
- return PERMISSION_DENIED;
- }
-
- Vector<String8> utf8Args;
- utf8Args.setCapacity(argc);
- for (uint32_t i = 0; i < argc; i++) {
- utf8Args.push(String8(argv[i]));
- }
-
- if (argc >= 1) {
- // adb shell cmd stats config ...
- if (!utf8Args[0].compare(String8("config"))) {
- return cmd_config(in, out, err, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("print-uid-map"))) {
- return cmd_print_uid_map(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("dump-report"))) {
- return cmd_dump_report(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) {
- return cmd_print_pulled_metrics(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("send-broadcast"))) {
- return cmd_trigger_broadcast(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("print-stats"))) {
- return cmd_print_stats(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("meminfo"))) {
- return cmd_dump_memory_info(out);
- }
-
- if (!utf8Args[0].compare(String8("write-to-disk"))) {
- return cmd_write_data_to_disk(out);
- }
-
- if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) {
- return cmd_log_app_breadcrumb(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("log-binary-push"))) {
- return cmd_log_binary_push(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("clear-puller-cache"))) {
- return cmd_clear_puller_cache(out);
- }
-
- if (!utf8Args[0].compare(String8("print-logs"))) {
- return cmd_print_logs(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("send-active-configs"))) {
- return cmd_trigger_active_config_broadcast(out, utf8Args);
- }
-
- if (!utf8Args[0].compare(String8("data-subscribe"))) {
- {
- std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
- if (mShellSubscriber == nullptr) {
- mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
- }
- }
- int timeoutSec = -1;
- if (argc >= 2) {
- timeoutSec = atoi(utf8Args[1].c_str());
- }
- mShellSubscriber->startNewSubscription(in, out, timeoutSec);
- return NO_ERROR;
- }
- }
-
- print_cmd_help(out);
- return NO_ERROR;
-}
-
-void StatsService::print_cmd_help(int out) {
- dprintf(out,
- "usage: adb shell cmd stats print-stats-log [tag_required] "
- "[timestamp_nsec_optional]\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats meminfo\n");
- dprintf(out, "\n");
- dprintf(out, " Prints the malloc debug information. You need to run the following first: \n");
- dprintf(out, " # adb shell stop\n");
- dprintf(out, " # adb shell setprop libc.debug.malloc.program statsd \n");
- dprintf(out, " # adb shell setprop libc.debug.malloc.options backtrace \n");
- dprintf(out, " # adb shell start\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats print-uid-map [PKG]\n");
- dprintf(out, "\n");
- dprintf(out, " Prints the UID, app name, version mapping.\n");
- dprintf(out, " PKG Optional package name to print the uids of the package\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats pull-source ATOM_TAG [PACKAGE] \n");
- dprintf(out, "\n");
- dprintf(out, " Prints the output of a pulled atom\n");
- dprintf(out, " UID The atom to pull\n");
- dprintf(out, " PACKAGE The package to pull from. Default is AID_SYSTEM\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats write-to-disk \n");
- dprintf(out, "\n");
- dprintf(out, " Flushes all data on memory to disk.\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats log-app-breadcrumb [UID] LABEL STATE\n");
- dprintf(out, " Writes an AppBreadcrumbReported event to the statslog buffer.\n");
- dprintf(out, " UID The uid to use. It is only possible to pass a UID\n");
- dprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
- dprintf(out, " uid is used.\n");
- dprintf(out, " LABEL Integer in [0, 15], as per atoms.proto.\n");
- dprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out,
- "usage: adb shell cmd stats log-binary-push NAME VERSION STAGING ROLLBACK_ENABLED "
- "LOW_LATENCY STATE EXPERIMENT_IDS\n");
- dprintf(out, " Log a binary push state changed event.\n");
- dprintf(out, " NAME The train name.\n");
- dprintf(out, " VERSION The train version code.\n");
- dprintf(out, " STAGING If this train requires a restart.\n");
- dprintf(out, " ROLLBACK_ENABLED If rollback should be enabled for this install.\n");
- dprintf(out, " LOW_LATENCY If the train requires low latency monitoring.\n");
- dprintf(out, " STATE The status of the train push.\n");
- dprintf(out, " Integer value of the enum in atoms.proto.\n");
- dprintf(out, " EXPERIMENT_IDS Comma separated list of experiment ids.\n");
- dprintf(out, " Leave blank for none.\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n");
- dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
- dprintf(out, "\n");
- dprintf(out, " Adds, updates or removes a configuration. The proto should be in\n");
- dprintf(out, " wire-encoded protobuf format and passed via stdin. If no UID and name is\n");
- dprintf(out, " provided, then all configs will be removed from memory and disk.\n");
- dprintf(out, "\n");
- dprintf(out, " UID The uid to use. It is only possible to pass the UID\n");
- dprintf(out, " parameter on eng builds. If UID is omitted the calling\n");
- dprintf(out, " uid is used.\n");
- dprintf(out, " NAME The per-uid name to use\n");
- dprintf(out, "\n");
- dprintf(out, "\n *Note: If both UID and NAME are omitted then all configs will\n");
- dprintf(out, "\n be removed from memory and disk!\n");
- dprintf(out, "\n");
- dprintf(out,
- "usage: adb shell cmd stats dump-report [UID] NAME [--keep_data] "
- "[--include_current_bucket] [--proto]\n");
- dprintf(out, " Dump all metric data for a configuration.\n");
- dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
- dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
- dprintf(out, " calling uid is used.\n");
- dprintf(out, " NAME The name of the configuration\n");
- dprintf(out, " --keep_data Do NOT erase the data upon dumping it.\n");
- dprintf(out, " --proto Print proto binary.\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n");
- dprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n");
- dprintf(out, " UID The uid of the configuration. It is only possible to pass\n");
- dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
- dprintf(out, " calling uid is used.\n");
- dprintf(out, " NAME The name of the configuration\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out,
- "usage: adb shell cmd stats send-active-configs [--uid=UID] [--configs] "
- "[NAME1] [NAME2] [NAME3..]\n");
- dprintf(out, " Send a broadcast that informs the subscriber of the current active configs.\n");
- dprintf(out, " --uid=UID The uid of the configurations. It is only possible to pass\n");
- dprintf(out, " the UID parameter on eng builds. If UID is omitted the\n");
- dprintf(out, " calling uid is used.\n");
- dprintf(out, " --configs Send the list of configs in the name list instead of\n");
- dprintf(out, " the currently active configs\n");
- dprintf(out, " NAME LIST List of configuration names to be included in the broadcast.\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats print-stats\n");
- dprintf(out, " Prints some basic stats.\n");
- dprintf(out, " --proto Print proto binary instead of string format.\n");
- dprintf(out, "\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats clear-puller-cache\n");
- dprintf(out, " Clear cached puller data.\n");
- dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats print-logs\n");
- dprintf(out, " Requires root privileges.\n");
- dprintf(out, " Can be disabled by calling adb shell cmd stats print-logs 0\n");
-}
-
-status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
- string name;
- bool good = false;
- int uid;
- const int argCount = args.size();
- if (argCount == 2) {
- // Automatically pick the UID
- uid = AIBinder_getCallingUid();
- name.assign(args[1].c_str(), args[1].size());
- good = true;
- } else if (argCount == 3) {
- good = getUidFromArgs(args, 1, uid);
- if (!good) {
- dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
- "other UIDs on eng or userdebug builds.\n");
- }
- name.assign(args[2].c_str(), args[2].size());
- }
- if (!good) {
- print_cmd_help(out);
- return UNKNOWN_ERROR;
- }
- ConfigKey key(uid, StrToInt64(name));
- shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
- if (receiver == nullptr) {
- VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
- return UNKNOWN_ERROR;
- } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) {
- VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
- args[2].c_str());
- } else {
- VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str());
- return UNKNOWN_ERROR;
- }
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<String8>& args) {
- const int argCount = args.size();
- int uid;
- vector<int64_t> configIds;
- if (argCount == 1) {
- // Automatically pick the uid and send a broadcast that has no active configs.
- uid = AIBinder_getCallingUid();
- mProcessor->GetActiveConfigs(uid, configIds);
- } else {
- int curArg = 1;
- if(args[curArg].find("--uid=") == 0) {
- string uidArgStr(args[curArg].c_str());
- string uidStr = uidArgStr.substr(6);
- if (!getUidFromString(uidStr.c_str(), uid)) {
- dprintf(out, "Invalid UID. Note that the config can only be set for "
- "other UIDs on eng or userdebug builds.\n");
- return UNKNOWN_ERROR;
- }
- curArg++;
- } else {
- uid = AIBinder_getCallingUid();
- }
- if (curArg == argCount || args[curArg] != "--configs") {
- VLOG("Reached end of args, or specify configs not set. Sending actual active configs,");
- mProcessor->GetActiveConfigs(uid, configIds);
- } else {
- // Flag specified, use the given list of configs.
- curArg++;
- for (int i = curArg; i < argCount; i++) {
- char* endp;
- int64_t configID = strtoll(args[i].c_str(), &endp, 10);
- if (endp == args[i].c_str() || *endp != '\0') {
- dprintf(out, "Error parsing config ID.\n");
- return UNKNOWN_ERROR;
- }
- VLOG("Adding config id %ld", static_cast<long>(configID));
- configIds.push_back(configID);
- }
- }
- }
- shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
- if (receiver == nullptr) {
- VLOG("Could not find receiver for uid %d", uid);
- return UNKNOWN_ERROR;
- } else if (receiver->sendActiveConfigsChangedBroadcast(configIds).isOk()) {
- VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid);
- } else {
- VLOG("StatsService::trigger active configs changed broadcast failed for uid %d", uid);
- return UNKNOWN_ERROR;
- }
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_config(int in, int out, int err, Vector<String8>& args) {
- const int argCount = args.size();
- if (argCount >= 2) {
- if (args[1] == "update" || args[1] == "remove") {
- bool good = false;
- int uid = -1;
- string name;
-
- if (argCount == 3) {
- // Automatically pick the UID
- uid = AIBinder_getCallingUid();
- name.assign(args[2].c_str(), args[2].size());
- good = true;
- } else if (argCount == 4) {
- good = getUidFromArgs(args, 2, uid);
- if (!good) {
- dprintf(err, "Invalid UID. Note that the config can only be set for "
- "other UIDs on eng or userdebug builds.\n");
- }
- name.assign(args[3].c_str(), args[3].size());
- } else if (argCount == 2 && args[1] == "remove") {
- good = true;
- }
-
- if (!good) {
- // If arg parsing failed, print the help text and return an error.
- print_cmd_help(out);
- return UNKNOWN_ERROR;
- }
-
- if (args[1] == "update") {
- char* endp;
- int64_t configID = strtoll(name.c_str(), &endp, 10);
- if (endp == name.c_str() || *endp != '\0') {
- dprintf(err, "Error parsing config ID.\n");
- return UNKNOWN_ERROR;
- }
-
- // Read stream into buffer.
- string buffer;
- if (!android::base::ReadFdToString(in, &buffer)) {
- dprintf(err, "Error reading stream for StatsConfig.\n");
- return UNKNOWN_ERROR;
- }
-
- // Parse buffer.
- StatsdConfig config;
- if (!config.ParseFromString(buffer)) {
- dprintf(err, "Error parsing proto stream for StatsConfig.\n");
- return UNKNOWN_ERROR;
- }
-
- // Add / update the config.
- mConfigManager->UpdateConfig(ConfigKey(uid, configID), config);
- } else {
- if (argCount == 2) {
- cmd_remove_all_configs(out);
- } else {
- // Remove the config.
- mConfigManager->RemoveConfig(ConfigKey(uid, StrToInt64(name)));
- }
- }
-
- return NO_ERROR;
- }
- }
- print_cmd_help(out);
- return UNKNOWN_ERROR;
-}
-
-status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
- if (mProcessor != nullptr) {
- int argCount = args.size();
- bool good = false;
- bool proto = false;
- bool includeCurrentBucket = false;
- bool eraseData = true;
- int uid;
- string name;
- if (!std::strcmp("--proto", args[argCount-1].c_str())) {
- proto = true;
- argCount -= 1;
- }
- if (!std::strcmp("--include_current_bucket", args[argCount-1].c_str())) {
- includeCurrentBucket = true;
- argCount -= 1;
- }
- if (!std::strcmp("--keep_data", args[argCount-1].c_str())) {
- eraseData = false;
- argCount -= 1;
- }
- if (argCount == 2) {
- // Automatically pick the UID
- uid = AIBinder_getCallingUid();
- name.assign(args[1].c_str(), args[1].size());
- good = true;
- } else if (argCount == 3) {
- good = getUidFromArgs(args, 1, uid);
- if (!good) {
- dprintf(out, "Invalid UID. Note that the metrics can only be dumped for "
- "other UIDs on eng or userdebug builds.\n");
- }
- name.assign(args[2].c_str(), args[2].size());
- }
- if (good) {
- vector<uint8_t> data;
- mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
- includeCurrentBucket, eraseData, ADB_DUMP,
- NO_TIME_CONSTRAINTS,
- &data);
- if (proto) {
- for (size_t i = 0; i < data.size(); i ++) {
- dprintf(out, "%c", data[i]);
- }
- } else {
- dprintf(out, "Non-proto stats data dump not currently supported.\n");
- }
- return android::OK;
- } else {
- // If arg parsing failed, print the help text and return an error.
- print_cmd_help(out);
- return UNKNOWN_ERROR;
- }
- } else {
- dprintf(out, "Log processor does not exist...\n");
- return UNKNOWN_ERROR;
- }
-}
-
-status_t StatsService::cmd_print_stats(int out, const Vector<String8>& args) {
- int argCount = args.size();
- bool proto = false;
- if (!std::strcmp("--proto", args[argCount-1].c_str())) {
- proto = true;
- argCount -= 1;
- }
- StatsdStats& statsdStats = StatsdStats::getInstance();
- if (proto) {
- vector<uint8_t> data;
- statsdStats.dumpStats(&data, false); // does not reset statsdStats.
- for (size_t i = 0; i < data.size(); i ++) {
- dprintf(out, "%c", data[i]);
- }
-
- } else {
- vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
- for (const ConfigKey& key : configs) {
- dprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
- mProcessor->GetMetricsSize(key));
- }
- statsdStats.dumpStats(out);
- }
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_print_uid_map(int out, const Vector<String8>& args) {
- if (args.size() > 1) {
- string pkg;
- pkg.assign(args[1].c_str(), args[1].size());
- auto uids = mUidMap->getAppUid(pkg);
- dprintf(out, "%s -> [ ", pkg.c_str());
- for (const auto& uid : uids) {
- dprintf(out, "%d ", uid);
- }
- dprintf(out, "]\n");
- } else {
- mUidMap->printUidMap(out);
- }
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_write_data_to_disk(int out) {
- dprintf(out, "Writing data to disk\n");
- mProcessor->WriteDataToDisk(ADB_DUMP, NO_TIME_CONSTRAINTS);
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& args) {
- bool good = false;
- int32_t uid;
- int32_t label;
- int32_t state;
- const int argCount = args.size();
- if (argCount == 3) {
- // Automatically pick the UID
- uid = AIBinder_getCallingUid();
- label = atoi(args[1].c_str());
- state = atoi(args[2].c_str());
- good = true;
- } else if (argCount == 4) {
- good = getUidFromArgs(args, 1, uid);
- if (!good) {
- dprintf(out,
- "Invalid UID. Note that selecting a UID for writing AppBreadcrumb can only be "
- "done for other UIDs on eng or userdebug builds.\n");
- }
- label = atoi(args[2].c_str());
- state = atoi(args[3].c_str());
- }
- if (good) {
- dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
- android::os::statsd::util::stats_write(
- android::os::statsd::util::APP_BREADCRUMB_REPORTED, uid, label, state);
- } else {
- print_cmd_help(out);
- return UNKNOWN_ERROR;
- }
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_log_binary_push(int out, const Vector<String8>& args) {
- // Security checks are done in the sendBinaryPushStateChanged atom.
- const int argCount = args.size();
- if (argCount != 7 && argCount != 8) {
- dprintf(out, "Incorrect number of argument supplied\n");
- return UNKNOWN_ERROR;
- }
- string trainName = string(args[1].c_str());
- int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10);
- int32_t state = atoi(args[6].c_str());
- vector<int64_t> experimentIds;
- if (argCount == 8) {
- vector<string> experimentIdsString = android::base::Split(string(args[7].c_str()), ",");
- for (string experimentIdString : experimentIdsString) {
- int64_t experimentId = strtoll(experimentIdString.c_str(), nullptr, 10);
- experimentIds.push_back(experimentId);
- }
- }
- dprintf(out, "Logging BinaryPushStateChanged\n");
- vector<uint8_t> experimentIdBytes;
- writeExperimentIdsToProto(experimentIds, &experimentIdBytes);
- LogEvent event(trainName, trainVersion, args[3], args[4], args[5], state, experimentIdBytes, 0);
- mProcessor->OnLogEvent(&event);
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
- int s = atoi(args[1].c_str());
- vector<int32_t> uids;
- if (args.size() > 2) {
- string package = string(args[2].c_str());
- auto it = UidMap::sAidToUidMapping.find(package);
- if (it != UidMap::sAidToUidMapping.end()) {
- uids.push_back(it->second);
- } else {
- set<int32_t> uids_set = mUidMap->getAppUid(package);
- uids.insert(uids.end(), uids_set.begin(), uids_set.end());
- }
- } else {
- uids.push_back(AID_SYSTEM);
- }
- vector<shared_ptr<LogEvent>> stats;
- if (mPullerManager->Pull(s, uids, getElapsedRealtimeNs(), &stats)) {
- for (const auto& it : stats) {
- dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
- }
- dprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
- return NO_ERROR;
- }
- return UNKNOWN_ERROR;
-}
-
-status_t StatsService::cmd_remove_all_configs(int out) {
- dprintf(out, "Removing all configs...\n");
- VLOG("StatsService::cmd_remove_all_configs was called");
- mConfigManager->RemoveAllConfigs();
- StorageManager::deleteAllFiles(STATS_SERVICE_DIR);
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_dump_memory_info(int out) {
- dprintf(out, "meminfo not available.\n");
- return NO_ERROR;
-}
-
-status_t StatsService::cmd_clear_puller_cache(int out) {
- VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
- AIBinder_getCallingPid(), AIBinder_getCallingUid());
- if (checkPermission(kPermissionDump)) {
- int cleared = mPullerManager->ForceClearPullerCache();
- dprintf(out, "Puller removed %d cached data!\n", cleared);
- return NO_ERROR;
- } else {
- return PERMISSION_DENIED;
- }
-}
-
-status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- Status status = checkUid(AID_ROOT);
- if (!status.isOk()) {
- return PERMISSION_DENIED;
- }
-
- VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(),
- AIBinder_getCallingUid());
- bool enabled = true;
- if (args.size() >= 2) {
- enabled = atoi(args[1].c_str()) != 0;
- }
- mProcessor->setPrintLogs(enabled);
- return NO_ERROR;
-}
-
-bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
- return getUidFromString(args[uidArgIndex].c_str(), uid);
-}
-
-bool StatsService::getUidFromString(const char* s, int32_t& uid) {
- if (*s == '\0') {
- return false;
- }
- char* endc = NULL;
- int64_t longUid = strtol(s, &endc, 0);
- if (*endc != '\0') {
- return false;
- }
- int32_t goodUid = static_cast<int32_t>(longUid);
- if (longUid < 0 || static_cast<uint64_t>(longUid) != static_cast<uid_t>(goodUid)) {
- return false; // It was not of uid_t type.
- }
- uid = goodUid;
-
- int32_t callingUid = AIBinder_getCallingUid();
- return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
- || (callingUid == goodUid) // Anyone can 'impersonate' themselves.
- || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
-}
-
-Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
- ENFORCE_UID(AID_SYSTEM);
- // Read stream into buffer.
- string buffer;
- if (!android::base::ReadFdToString(fd.get(), &buffer)) {
- return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
- }
-
- // Parse buffer.
- UidData uidData;
- if (!uidData.ParseFromString(buffer)) {
- return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
- }
-
- vector<String16> versionStrings;
- vector<String16> installers;
- vector<String16> packageNames;
- vector<int32_t> uids;
- vector<int64_t> versions;
-
- const auto numEntries = uidData.app_info_size();
- versionStrings.reserve(numEntries);
- installers.reserve(numEntries);
- packageNames.reserve(numEntries);
- uids.reserve(numEntries);
- versions.reserve(numEntries);
-
- for (const auto& appInfo: uidData.app_info()) {
- packageNames.emplace_back(String16(appInfo.package_name().c_str()));
- uids.push_back(appInfo.uid());
- versions.push_back(appInfo.version());
- versionStrings.emplace_back(String16(appInfo.version_string().c_str()));
- installers.emplace_back(String16(appInfo.installer().c_str()));
- }
-
- mUidMap->updateMap(getElapsedRealtimeNs(),
- uids,
- versions,
- versionStrings,
- packageNames,
- installers);
-
- mBootCompleteTrigger.markComplete(kUidMapReceivedTag);
- VLOG("StatsService::informAllUidData UidData proto parsed successfully.");
- return Status::ok();
-}
-
-Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version,
- const string& versionString, const string& installer) {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::informOnePackage was called");
- String16 utf16App = String16(app.c_str());
- String16 utf16VersionString = String16(versionString.c_str());
- String16 utf16Installer = String16(installer.c_str());
-
- mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString,
- utf16Installer);
- return Status::ok();
-}
-
-Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::informOnePackageRemoved was called");
- String16 utf16App = String16(app.c_str());
- mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid);
- mConfigManager->RemoveConfigs(uid);
- return Status::ok();
-}
-
-Status StatsService::informAlarmForSubscriberTriggeringFired() {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called");
- int64_t currentTimeSec = getElapsedRealtimeSec();
- std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
- mPeriodicAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
- if (alarmSet.size() > 0) {
- VLOG("Found periodic alarm fired.");
- mProcessor->onPeriodicAlarmFired(currentTimeSec * NS_PER_SEC, alarmSet);
- } else {
- ALOGW("Cannot find an periodic alarm that fired. Perhaps it was recently cancelled.");
- }
- return Status::ok();
-}
-
-Status StatsService::informPollAlarmFired() {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::informPollAlarmFired was called");
- mProcessor->informPullAlarmFired(getElapsedRealtimeNs());
- VLOG("StatsService::informPollAlarmFired succeeded");
- return Status::ok();
-}
-
-Status StatsService::systemRunning() {
- ENFORCE_UID(AID_SYSTEM);
-
- // When system_server is up and running, schedule the dropbox task to run.
- VLOG("StatsService::systemRunning");
- sayHiToStatsCompanion();
- return Status::ok();
-}
-
-Status StatsService::informDeviceShutdown() {
- ENFORCE_UID(AID_SYSTEM);
- VLOG("StatsService::informDeviceShutdown");
- mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST);
- mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
- mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs());
- return Status::ok();
-}
-
-void StatsService::sayHiToStatsCompanion() {
- shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
- if (statsCompanion != nullptr) {
- VLOG("Telling statsCompanion that statsd is ready");
- statsCompanion->statsdReady();
- } else {
- VLOG("Could not access statsCompanion");
- }
-}
-
-Status StatsService::statsCompanionReady() {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::statsCompanionReady was called");
- shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
- if (statsCompanion == nullptr) {
- return exception(EX_NULL_POINTER,
- "StatsCompanion unavailable despite it contacting statsd.");
- }
- VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
- AIBinder_linkToDeath(statsCompanion->asBinder().get(),
- mStatsCompanionServiceDeathRecipient.get(), this);
- mPullerManager->SetStatsCompanionService(statsCompanion);
- mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
- mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
- return Status::ok();
-}
-
-Status StatsService::bootCompleted() {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::bootCompleted was called");
- mBootCompleteTrigger.markComplete(kBootCompleteTag);
- return Status::ok();
-}
-
-void StatsService::Startup() {
- mConfigManager->Startup();
- mProcessor->LoadActiveConfigsFromDisk();
- mProcessor->LoadMetadataFromDisk(getWallClockNs(), getElapsedRealtimeNs());
-}
-
-void StatsService::Terminate() {
- ALOGI("StatsService::Terminating");
- if (mProcessor != nullptr) {
- mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST);
- mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
- mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs());
- }
-}
-
-// Test only interface!!!
-void StatsService::OnLogEvent(LogEvent* event) {
- mProcessor->OnLogEvent(event);
- if (mShellSubscriber != nullptr) {
- mShellSubscriber->onLogEvent(*event);
- }
-}
-
-Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::getData with Uid %i", callingUid);
- ConfigKey configKey(callingUid, key);
- // The dump latency does not matter here since we do not include the current bucket, we do not
- // need to pull any new data anyhow.
- mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
- true /* erase_data */, GET_DATA_CALLED, FAST, output);
- return Status::ok();
-}
-
-Status StatsService::getMetadata(vector<uint8_t>* output) {
- ENFORCE_UID(AID_SYSTEM);
-
- StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
- return Status::ok();
-}
-
-Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
- const int32_t callingUid) {
- ENFORCE_UID(AID_SYSTEM);
-
- if (addConfigurationChecked(callingUid, key, config)) {
- return Status::ok();
- } else {
- return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig.");
- }
-}
-
-bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) {
- ConfigKey configKey(uid, key);
- StatsdConfig cfg;
- if (config.size() > 0) { // If the config is empty, skip parsing.
- if (!cfg.ParseFromArray(&config[0], config.size())) {
- return false;
- }
- }
- mConfigManager->UpdateConfig(configKey, cfg);
- return true;
-}
-
-Status StatsService::removeDataFetchOperation(int64_t key,
- const int32_t callingUid) {
- ENFORCE_UID(AID_SYSTEM);
- ConfigKey configKey(callingUid, key);
- mConfigManager->RemoveConfigReceiver(configKey);
- return Status::ok();
-}
-
-Status StatsService::setDataFetchOperation(int64_t key,
- const shared_ptr<IPendingIntentRef>& pir,
- const int32_t callingUid) {
- ENFORCE_UID(AID_SYSTEM);
-
- ConfigKey configKey(callingUid, key);
- mConfigManager->SetConfigReceiver(configKey, pir);
- if (StorageManager::hasConfigMetricsReport(configKey)) {
- VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk",
- configKey.ToString().c_str());
- mProcessor->noteOnDiskData(configKey);
- }
- return Status::ok();
-}
-
-Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
- const int32_t callingUid,
- vector<int64_t>* output) {
- ENFORCE_UID(AID_SYSTEM);
-
- mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir);
- if (output != nullptr) {
- mProcessor->GetActiveConfigs(callingUid, *output);
- } else {
- ALOGW("StatsService::setActiveConfigsChanged output was nullptr");
- }
- return Status::ok();
-}
-
-Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) {
- ENFORCE_UID(AID_SYSTEM);
-
- mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid);
- return Status::ok();
-}
-
-Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) {
- ENFORCE_UID(AID_SYSTEM);
-
- ConfigKey configKey(callingUid, key);
- mConfigManager->RemoveConfig(configKey);
- return Status::ok();
-}
-
-Status StatsService::setBroadcastSubscriber(int64_t configId,
- int64_t subscriberId,
- const shared_ptr<IPendingIntentRef>& pir,
- const int32_t callingUid) {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::setBroadcastSubscriber called.");
- ConfigKey configKey(callingUid, configId);
- SubscriberReporter::getInstance()
- .setBroadcastSubscriber(configKey, subscriberId, pir);
- return Status::ok();
-}
-
-Status StatsService::unsetBroadcastSubscriber(int64_t configId,
- int64_t subscriberId,
- const int32_t callingUid) {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::unsetBroadcastSubscriber called.");
- ConfigKey configKey(callingUid, configId);
- SubscriberReporter::getInstance()
- .unsetBroadcastSubscriber(configKey, subscriberId);
- return Status::ok();
-}
-
-Status StatsService::allPullersFromBootRegistered() {
- ENFORCE_UID(AID_SYSTEM);
-
- VLOG("StatsService::allPullersFromBootRegistered was called");
- mBootCompleteTrigger.markComplete(kAllPullersRegisteredTag);
- return Status::ok();
-}
-
-Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownMillis,
- int64_t timeoutMillis,
- const std::vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& pullerCallback) {
- ENFORCE_UID(AID_SYSTEM);
- VLOG("StatsService::registerPullAtomCallback called.");
- mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis),
- MillisToNano(timeoutMillis), additiveFields,
- pullerCallback);
- return Status::ok();
-}
-
-Status StatsService::registerNativePullAtomCallback(
- int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
- const std::vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& pullerCallback) {
- if (!checkPermission(kPermissionRegisterPullAtom)) {
- return exception(
- EX_SECURITY,
- StringPrintf("Uid %d does not have the %s permission when registering atom %d",
- AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag));
- }
- VLOG("StatsService::registerNativePullAtomCallback called.");
- int32_t uid = AIBinder_getCallingUid();
- mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis),
- MillisToNano(timeoutMillis), additiveFields,
- pullerCallback);
- return Status::ok();
-}
-
-Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) {
- ENFORCE_UID(AID_SYSTEM);
- VLOG("StatsService::unregisterPullAtomCallback called.");
- mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
- return Status::ok();
-}
-
-Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
- if (!checkPermission(kPermissionRegisterPullAtom)) {
- return exception(
- EX_SECURITY,
- StringPrintf("Uid %d does not have the %s permission when unregistering atom %d",
- AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag));
- }
- VLOG("StatsService::unregisterNativePullAtomCallback called.");
- int32_t uid = AIBinder_getCallingUid();
- mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
- return Status::ok();
-}
-
-Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
- ENFORCE_UID(AID_SYSTEM);
- // TODO: add verifier permission
-
- experimentIdsOut->clear();
- // Read the latest train info
- vector<InstallTrainInfo> trainInfoList = StorageManager::readAllTrainInfo();
- if (trainInfoList.empty()) {
- // No train info means no experiment IDs, return an empty list
- return Status::ok();
- }
-
- // Copy the experiment IDs to the out vector
- for (InstallTrainInfo& trainInfo : trainInfoList) {
- experimentIdsOut->insert(experimentIdsOut->end(),
- trainInfo.experimentIds.begin(),
- trainInfo.experimentIds.end());
- }
- return Status::ok();
-}
-
-void StatsService::statsCompanionServiceDied(void* cookie) {
- auto thiz = static_cast<StatsService*>(cookie);
- thiz->statsCompanionServiceDiedImpl();
-}
-
-void StatsService::statsCompanionServiceDiedImpl() {
- ALOGW("statscompanion service died");
- StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
- if (mProcessor != nullptr) {
- ALOGW("Reset statsd upon system server restarts.");
- int64_t systemServerRestartNs = getElapsedRealtimeNs();
- ProtoOutputStream activeConfigsProto;
- mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs,
- STATSCOMPANION_DIED, &activeConfigsProto);
- metadata::StatsMetadataList metadataList;
- mProcessor->WriteMetadataToProto(getWallClockNs(),
- systemServerRestartNs, &metadataList);
- mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST);
- mProcessor->resetConfigs();
-
- std::string serializedActiveConfigs;
- if (activeConfigsProto.serializeToString(&serializedActiveConfigs)) {
- ActiveConfigList activeConfigs;
- if (activeConfigs.ParseFromString(serializedActiveConfigs)) {
- mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs);
- }
- }
- mProcessor->SetMetadataState(metadataList, getWallClockNs(), systemServerRestartNs);
- }
- mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
- mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
- mPullerManager->SetStatsCompanionService(nullptr);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
deleted file mode 100644
index 479f4e87ec96..000000000000
--- a/cmds/statsd/src/StatsService.h
+++ /dev/null
@@ -1,416 +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.
- */
-
-#ifndef STATS_SERVICE_H
-#define STATS_SERVICE_H
-
-#include <aidl/android/os/BnStatsd.h>
-#include <aidl/android/os/IPendingIntentRef.h>
-#include <aidl/android/os/IPullAtomCallback.h>
-#include <gtest/gtest_prod.h>
-#include <utils/Looper.h>
-
-#include <mutex>
-
-#include "StatsLogProcessor.h"
-#include "anomaly/AlarmMonitor.h"
-#include "config/ConfigManager.h"
-#include "external/StatsPullerManager.h"
-#include "logd/LogEventQueue.h"
-#include "packages/UidMap.h"
-#include "shell/ShellSubscriber.h"
-#include "statscompanion_util.h"
-#include "utils/MultiConditionTrigger.h"
-
-using namespace android;
-using namespace android::os;
-using namespace std;
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::os::BnStatsd;
-using aidl::android::os::IPendingIntentRef;
-using aidl::android::os::IPullAtomCallback;
-using ::ndk::ScopedAIBinder_DeathRecipient;
-using ::ndk::ScopedFileDescriptor;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StatsService : public BnStatsd {
-public:
- StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
- virtual ~StatsService();
-
- /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
- const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
-
- virtual status_t dump(int fd, const char** args, uint32_t numArgs) override;
- virtual status_t handleShellCommand(int in, int out, int err, const char** argv,
- uint32_t argc) override;
-
- virtual Status systemRunning();
- virtual Status statsCompanionReady();
- virtual Status bootCompleted();
- virtual Status informPollAlarmFired();
- virtual Status informAlarmForSubscriberTriggeringFired();
-
- virtual Status informAllUidData(const ScopedFileDescriptor& fd);
- virtual Status informOnePackage(const string& app, int32_t uid, int64_t version,
- const string& versionString, const string& installer);
- virtual Status informOnePackageRemoved(const string& app, int32_t uid);
- virtual Status informDeviceShutdown();
-
- /**
- * Called right before we start processing events.
- */
- void Startup();
-
- /**
- * Called when terminiation signal received.
- */
- void Terminate();
-
- /**
- * Test ONLY interface. In real world, StatsService reads from LogEventQueue.
- */
- virtual void OnLogEvent(LogEvent* event);
-
- /**
- * Binder call for clients to request data for this configuration key.
- */
- virtual Status getData(int64_t key,
- const int32_t callingUid,
- vector<uint8_t>* output) override;
-
-
- /**
- * Binder call for clients to get metadata across all configs in statsd.
- */
- virtual Status getMetadata(vector<uint8_t>* output) override;
-
-
- /**
- * Binder call to let clients send a configuration and indicate they're interested when they
- * should requestData for this configuration.
- */
- virtual Status addConfiguration(int64_t key,
- const vector<uint8_t>& config,
- const int32_t callingUid) override;
-
- /**
- * Binder call to let clients register the data fetch operation for a configuration.
- */
- virtual Status setDataFetchOperation(int64_t key,
- const shared_ptr<IPendingIntentRef>& pir,
- const int32_t callingUid) override;
-
- /**
- * Binder call to remove the data fetch operation for the specified config key.
- */
- virtual Status removeDataFetchOperation(int64_t key,
- const int32_t callingUid) override;
-
- /**
- * Binder call to let clients register the active configs changed operation.
- */
- virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
- const int32_t callingUid,
- vector<int64_t>* output) override;
-
- /**
- * Binder call to remove the active configs changed operation for the specified package..
- */
- virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
- /**
- * Binder call to allow clients to remove the specified configuration.
- */
- virtual Status removeConfiguration(int64_t key,
- const int32_t callingUid) override;
-
- /**
- * Binder call to associate the given config's subscriberId with the given pendingIntentRef.
- */
- virtual Status setBroadcastSubscriber(int64_t configId,
- int64_t subscriberId,
- const shared_ptr<IPendingIntentRef>& pir,
- const int32_t callingUid) override;
-
- /**
- * Binder call to unassociate the given config's subscriberId with any pendingIntentRef.
- */
- virtual Status unsetBroadcastSubscriber(int64_t configId,
- int64_t subscriberId,
- const int32_t callingUid) override;
-
- /** Inform statsCompanion that statsd is ready. */
- virtual void sayHiToStatsCompanion();
-
- /**
- * Binder call to notify statsd that all pullers from boot have been registered.
- */
- virtual Status allPullersFromBootRegistered();
-
- /**
- * Binder call to register a callback function for a pulled atom.
- */
- virtual Status registerPullAtomCallback(
- int32_t uid, int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
- const std::vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& pullerCallback) override;
-
- /**
- * Binder call to register a callback function for a pulled atom.
- */
- virtual Status registerNativePullAtomCallback(
- int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
- const std::vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& pullerCallback) override;
-
- /**
- * Binder call to unregister any existing callback for the given uid and atom.
- */
- virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override;
-
- /**
- * Binder call to unregister any existing callback for the given atom and calling uid.
- */
- virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
-
- /**
- * Binder call to get registered experiment IDs.
- */
- virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
-
-private:
- /**
- * Load system properties at init.
- */
- void init_system_properties();
-
- /**
- * Helper for loading system properties.
- */
- static void init_build_type_callback(void* cookie, const char* name, const char* value,
- uint32_t serial);
-
- /**
- * Proto output of statsd report data dumpsys, wrapped in a StatsDataDumpProto.
- */
- void dumpIncidentSection(int outFd);
-
- /**
- * Text or proto output of statsdStats dumpsys.
- */
- void dumpStatsdStats(int outFd, bool verbose, bool proto);
-
- /**
- * Print usage information for the commands
- */
- void print_cmd_help(int out);
-
- /* Runs on its dedicated thread to process pushed stats event from socket. */
- void readLogs();
-
- /**
- * Trigger a broadcast.
- */
- status_t cmd_trigger_broadcast(int outFd, Vector<String8>& args);
-
-
- /**
- * Trigger an active configs changed broadcast.
- */
- status_t cmd_trigger_active_config_broadcast(int outFd, Vector<String8>& args);
-
- /**
- * Handle the config sub-command.
- */
- status_t cmd_config(int inFd, int outFd, int err, Vector<String8>& args);
-
- /**
- * Prints some basic stats to std out.
- */
- status_t cmd_print_stats(int outFd, const Vector<String8>& args);
-
- /**
- * Print the event log.
- */
- status_t cmd_dump_report(int outFd, const Vector<String8>& args);
-
- /**
- * Print the mapping of uids to package names.
- */
- status_t cmd_print_uid_map(int outFd, const Vector<String8>& args);
-
- /**
- * Flush the data to disk.
- */
- status_t cmd_write_data_to_disk(int outFd);
-
- /**
- * Write an AppBreadcrumbReported event to the StatsLog buffer, as if calling
- * StatsLog.write(APP_BREADCRUMB_REPORTED).
- */
- status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args);
-
- /**
- * Write an BinaryPushStateChanged event, as if calling StatsLog.logBinaryPushStateChanged().
- */
- status_t cmd_log_binary_push(int outFd, const Vector<String8>& args);
-
- /**
- * Print contents of a pulled metrics source.
- */
- status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args);
-
- /**
- * Removes all configs stored on disk and on memory.
- */
- status_t cmd_remove_all_configs(int outFd);
-
- /*
- * Dump memory usage by statsd.
- */
- status_t cmd_dump_memory_info(int outFd);
-
- /*
- * Clear all puller cached data
- */
- status_t cmd_clear_puller_cache(int outFd);
-
- /**
- * Print all stats logs received to logcat.
- */
- status_t cmd_print_logs(int outFd, const Vector<String8>& args);
-
- /**
- * Writes the value of args[uidArgIndex] into uid.
- * Returns whether the uid is reasonable (type uid_t) and whether
- * 1. it is equal to the calling uid, or
- * 2. the device is mEngBuild, or
- * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
- */
- bool getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid);
-
- /**
- * Writes the value of uidSting into uid.
- * Returns whether the uid is reasonable (type uid_t) and whether
- * 1. it is equal to the calling uid, or
- * 2. the device is mEngBuild, or
- * 3. the caller is AID_ROOT and the uid is AID_SHELL (i.e. ROOT can impersonate SHELL).
- */
- bool getUidFromString(const char* uidString, int32_t& uid);
-
- /**
- * Adds a configuration after checking permissions and obtaining UID from binder call.
- */
- bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
-
- /**
- * Update a configuration.
- */
- void set_config(int uid, const string& name, const StatsdConfig& config);
-
- /**
- * Death recipient callback that is called when StatsCompanionService dies.
- * The cookie is a pointer to a StatsService object.
- */
- static void statsCompanionServiceDied(void* cookie);
-
- /**
- * Implementation of statsCompanionServiceDied.
- */
- void statsCompanionServiceDiedImpl();
-
- /**
- * Tracks the uid <--> package name mapping.
- */
- sp<UidMap> mUidMap;
-
- /**
- * Fetches external metrics
- */
- sp<StatsPullerManager> mPullerManager;
-
- /**
- * Tracks the configurations that have been passed to statsd.
- */
- sp<ConfigManager> mConfigManager;
-
- /**
- * The metrics recorder.
- */
- sp<StatsLogProcessor> mProcessor;
-
- /**
- * The alarm monitor for anomaly detection.
- */
- const sp<AlarmMonitor> mAnomalyAlarmMonitor;
-
- /**
- * The alarm monitor for alarms to directly trigger subscriber.
- */
- const sp<AlarmMonitor> mPeriodicAlarmMonitor;
-
- /**
- * Whether this is an eng build.
- */
- bool mEngBuild;
-
- sp<ShellSubscriber> mShellSubscriber;
-
- /**
- * Mutex for setting the shell subscriber
- */
- mutable mutex mShellSubscriberMutex;
- std::shared_ptr<LogEventQueue> mEventQueue;
-
- MultiConditionTrigger mBootCompleteTrigger;
- static const inline string kBootCompleteTag = "BOOT_COMPLETE";
- static const inline string kUidMapReceivedTag = "UID_MAP";
- static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED";
-
- ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
-
- FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
- FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
- FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
- FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
- FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
- FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
- FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot);
- FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
- FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
- FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit);
- FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket);
- FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket);
- FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket);
- FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket);
- FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
- FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket);
-
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // STATS_SERVICE_H
diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto
deleted file mode 100644
index 992983358ae6..000000000000
--- a/cmds/statsd/src/active_config_list.proto
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto2";
-
-package android.os.statsd;
-option java_package = "com.android.os";
-option java_multiple_files = true;
-option java_outer_classname = "ActiveConfigProto";
-
-message ActiveEventActivation {
- optional int32 atom_matcher_index = 1;
-
- // Time left in activation. When this proto is loaded after device boot,
- // the activation should be set to active for this duration.
- // This field will only be set when the state is ACTIVE
- optional int64 remaining_ttl_nanos = 2;
-
- enum State {
- UNNKNOWN = 0;
- // This metric should activate for remaining_ttl_nanos when we load the activations.
- ACTIVE = 1;
- // When we load the activations, this metric should activate on next boot for the tll
- // specified in the config.
- ACTIVATE_ON_BOOT = 2;
- }
- optional State state = 3;
-}
-
-message ActiveMetric {
- optional int64 id = 1;
- repeated ActiveEventActivation activation = 2;
-}
-
-message ActiveConfig {
- optional int64 id = 1;
- optional int32 uid = 2;
- repeated ActiveMetric metric = 3;
-}
-
-// all configs and their metrics on device.
-message ActiveConfigList {
- repeated ActiveConfig config = 1;
-}
diff --git a/cmds/statsd/src/annotations.h b/cmds/statsd/src/annotations.h
deleted file mode 100644
index cf7f5433663f..000000000000
--- a/cmds/statsd/src/annotations.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const uint8_t ANNOTATION_ID_IS_UID = 1;
-const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
-const uint8_t ANNOTATION_ID_PRIMARY_FIELD = 3;
-const uint8_t ANNOTATION_ID_EXCLUSIVE_STATE = 4;
-const uint8_t ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
-const uint8_t ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
-const uint8_t ANNOTATION_ID_STATE_NESTED = 8;
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
deleted file mode 100644
index b632d040eb43..000000000000
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ /dev/null
@@ -1,139 +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.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include "anomaly/AlarmMonitor.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-AlarmMonitor::AlarmMonitor(
- uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm,
- const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm)
- : mRegisteredAlarmTimeSec(0),
- mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
- mUpdateAlarm(updateAlarm),
- mCancelAlarm(cancelAlarm) {}
-
-AlarmMonitor::~AlarmMonitor() {}
-
-void AlarmMonitor::setStatsCompanionService(
- shared_ptr<IStatsCompanionService> statsCompanionService) {
- std::lock_guard<std::mutex> lock(mLock);
- shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
- mStatsCompanionService = statsCompanionService;
- if (statsCompanionService == nullptr) {
- VLOG("Erasing link to statsCompanionService");
- return;
- }
- VLOG("Creating link to statsCompanionService");
- const sp<const InternalAlarm> top = mPq.top();
- if (top != nullptr) {
- updateRegisteredAlarmTime_l(top->timestampSec);
- }
-}
-
-void AlarmMonitor::add(sp<const InternalAlarm> alarm) {
- std::lock_guard<std::mutex> lock(mLock);
- if (alarm == nullptr) {
- ALOGW("Asked to add a null alarm.");
- return;
- }
- if (alarm->timestampSec < 1) {
- // forbidden since a timestamp 0 is used to indicate no alarm registered
- ALOGW("Asked to add a 0-time alarm.");
- return;
- }
- // TODO(b/110563466): Ensure that refractory period is respected.
- VLOG("Adding alarm with time %u", alarm->timestampSec);
- mPq.push(alarm);
- if (mRegisteredAlarmTimeSec < 1 ||
- alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
- updateRegisteredAlarmTime_l(alarm->timestampSec);
- }
-}
-
-void AlarmMonitor::remove(sp<const InternalAlarm> alarm) {
- std::lock_guard<std::mutex> lock(mLock);
- if (alarm == nullptr) {
- ALOGW("Asked to remove a null alarm.");
- return;
- }
- VLOG("Removing alarm with time %u", alarm->timestampSec);
- bool wasPresent = mPq.remove(alarm);
- if (!wasPresent) return;
- if (mPq.empty()) {
- VLOG("Queue is empty. Cancel any alarm.");
- cancelRegisteredAlarmTime_l();
- return;
- }
- uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
- VLOG("Soonest alarm is %u", soonestAlarmTimeSec);
- if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) {
- updateRegisteredAlarmTime_l(soonestAlarmTimeSec);
- }
-}
-
-// More efficient than repeatedly calling remove(mPq.top()) since it batches the
-// updates to the registered alarm.
-unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> AlarmMonitor::popSoonerThan(
- uint32_t timestampSec) {
- VLOG("Removing alarms with time <= %u", timestampSec);
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> oldAlarms;
- std::lock_guard<std::mutex> lock(mLock);
-
- for (sp<const InternalAlarm> t = mPq.top(); t != nullptr && t->timestampSec <= timestampSec;
- t = mPq.top()) {
- oldAlarms.insert(t);
- mPq.pop(); // remove t
- }
- // Always update registered alarm time (if anything has changed).
- if (!oldAlarms.empty()) {
- if (mPq.empty()) {
- VLOG("Queue is empty. Cancel any alarm.");
- cancelRegisteredAlarmTime_l();
- } else {
- // Always update the registered alarm in this case (unlike remove()).
- updateRegisteredAlarmTime_l(mPq.top()->timestampSec);
- }
- }
- return oldAlarms;
-}
-
-void AlarmMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
- VLOG("Updating reg alarm time to %u", timestampSec);
- mRegisteredAlarmTimeSec = timestampSec;
- mUpdateAlarm(mStatsCompanionService, secToMs(mRegisteredAlarmTimeSec));
-}
-
-void AlarmMonitor::cancelRegisteredAlarmTime_l() {
- VLOG("Cancelling reg alarm.");
- mRegisteredAlarmTimeSec = 0;
- mCancelAlarm(mStatsCompanionService);
-}
-
-int64_t AlarmMonitor::secToMs(uint32_t timeSec) {
- return ((int64_t)timeSec) * 1000;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
deleted file mode 100644
index 5c34e381ba0b..000000000000
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ /dev/null
@@ -1,162 +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.
- */
-
-#pragma once
-
-#include "anomaly/indexed_priority_queue.h"
-
-#include <aidl/android/os/IStatsCompanionService.h>
-#include <utils/RefBase.h>
-
-#include <unordered_set>
-#include <vector>
-
-using namespace android;
-
-using aidl::android::os::IStatsCompanionService;
-using std::function;
-using std::shared_ptr;
-using std::unordered_set;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Represents an alarm, associated with some aggregate metric, holding a
- * projected time at which the metric is expected to exceed its anomaly
- * threshold.
- * Timestamps are in seconds since epoch in a uint32, so will fail in year 2106.
- */
-struct InternalAlarm : public RefBase {
- explicit InternalAlarm(uint32_t timestampSec) : timestampSec(timestampSec) {
- }
-
- const uint32_t timestampSec;
-
- /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
- struct SmallerTimestamp {
- bool operator()(sp<const InternalAlarm> a, sp<const InternalAlarm> b) const {
- return (a->timestampSec < b->timestampSec);
- }
- };
-};
-
-/**
- * Manages internal alarms that may get registered with the AlarmManager.
- */
-class AlarmMonitor : public RefBase {
-public:
- /**
- * @param minDiffToUpdateRegisteredAlarmTimeSec If the soonest alarm differs
- * from the registered alarm by more than this amount, update the registered
- * alarm.
- */
- AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>&
- updateAlarm,
- const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm);
- ~AlarmMonitor();
-
- /**
- * Tells AnomalyMonitor what IStatsCompanionService to use and, if
- * applicable, immediately registers an existing alarm with it.
- * If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
- * update IStatsCompanionService (until such time as it is set non-null).
- */
- void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
-
- /**
- * Adds the given alarm (reference) to the queue.
- */
- void add(sp<const InternalAlarm> alarm);
-
- /**
- * Removes the given alarm (reference) from the queue.
- * Note that alarm comparison is reference-based; if another alarm exists
- * with the same timestampSec, that alarm will still remain in the queue.
- */
- void remove(sp<const InternalAlarm> alarm);
-
- /**
- * Returns and removes all alarms whose timestamp <= the given timestampSec.
- * Always updates the registered alarm if return is non-empty.
- */
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> popSoonerThan(
- uint32_t timestampSec);
-
- /**
- * Returns the projected alarm timestamp that is registered with
- * StatsCompanionService. This may not be equal to the soonest alarm,
- * but should be within minDiffToUpdateRegisteredAlarmTimeSec of it.
- */
- uint32_t getRegisteredAlarmTimeSec() const {
- return mRegisteredAlarmTimeSec;
- }
-
-private:
- std::mutex mLock;
-
- /**
- * Timestamp (seconds since epoch) of the alarm registered with
- * StatsCompanionService. This, in general, may not be equal to the soonest
- * alarm stored in mPq, but should be within minUpdateTimeSec of it.
- * A value of 0 indicates that no alarm is currently registered.
- */
- uint32_t mRegisteredAlarmTimeSec;
-
- /**
- * Priority queue of alarms, prioritized by soonest alarm.timestampSec.
- */
- indexed_priority_queue<InternalAlarm, InternalAlarm::SmallerTimestamp> mPq;
-
- /**
- * Binder interface for communicating with StatsCompanionService.
- */
- shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
-
- /**
- * Amount by which the soonest projected alarm must differ from
- * mRegisteredAlarmTimeSec before updateRegisteredAlarmTime_l is called.
- */
- uint32_t mMinUpdateTimeSec;
-
- /**
- * Updates the alarm registered with StatsCompanionService to the given time.
- * Also correspondingly updates mRegisteredAlarmTimeSec.
- */
- void updateRegisteredAlarmTime_l(uint32_t timestampSec);
-
- /**
- * Cancels the alarm registered with StatsCompanionService.
- * Also correspondingly sets mRegisteredAlarmTimeSec to 0.
- */
- void cancelRegisteredAlarmTime_l();
-
- /** Converts uint32 timestamp in seconds to a Java long in msec. */
- int64_t secToMs(uint32_t timeSec);
-
- // Callback function to update the alarm via StatsCompanionService.
- std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm;
-
- // Callback function to cancel the alarm via StatsCompanionService.
- std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm;
-
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
deleted file mode 100644
index 6d9beb8f718d..000000000000
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "anomaly/AlarmTracker.h"
-#include "anomaly/subscriber_util.h"
-#include "HashableDimensionKey.h"
-#include "stats_util.h"
-#include "storage/StorageManager.h"
-
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-AlarmTracker::AlarmTracker(const int64_t startMillis,
- const int64_t currentMillis,
- const Alarm& alarm, const ConfigKey& configKey,
- const sp<AlarmMonitor>& alarmMonitor)
- : mAlarmConfig(alarm),
- mConfigKey(configKey),
- mAlarmMonitor(alarmMonitor) {
- VLOG("AlarmTracker() called");
- mAlarmSec = (startMillis + mAlarmConfig.offset_millis()) / MS_PER_SEC;
- // startMillis is the time statsd is created. We need to find the 1st alarm timestamp after
- // the config is added to statsd.
- mAlarmSec = findNextAlarmSec(currentMillis / MS_PER_SEC); // round up
- mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
- VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec);
- if (mAlarmMonitor != nullptr) {
- mAlarmMonitor->add(mInternalAlarm);
- }
-}
-
-AlarmTracker::~AlarmTracker() {
- VLOG("~AlarmTracker() called");
- if (mInternalAlarm != nullptr && mAlarmMonitor != nullptr) {
- mAlarmMonitor->remove(mInternalAlarm);
- }
-}
-
-void AlarmTracker::addSubscription(const Subscription& subscription) {
- mSubscriptions.push_back(subscription);
-}
-
-int64_t AlarmTracker::findNextAlarmSec(int64_t currentTimeSec) {
- if (currentTimeSec < mAlarmSec) {
- return mAlarmSec;
- }
- int64_t periodsForward =
- ((currentTimeSec - mAlarmSec) * MS_PER_SEC) / mAlarmConfig.period_millis() + 1;
- return mAlarmSec + periodsForward * mAlarmConfig.period_millis() / MS_PER_SEC;
-}
-
-void AlarmTracker::informAlarmsFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
- if (firedAlarms.empty() || mInternalAlarm == nullptr ||
- firedAlarms.find(mInternalAlarm) == firedAlarms.end()) {
- return;
- }
- if (!mSubscriptions.empty()) {
- VLOG("AlarmTracker triggers the subscribers.");
- triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY,
- 0 /* metricValue N/A */, mConfigKey, mSubscriptions);
- }
- firedAlarms.erase(mInternalAlarm);
- mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up
- mInternalAlarm = new InternalAlarm{static_cast<uint32_t>(mAlarmSec)};
- VLOG("AlarmTracker sets the periodic alarm at: %lld", (long long)mAlarmSec);
- if (mAlarmMonitor != nullptr) {
- mAlarmMonitor->add(mInternalAlarm);
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
deleted file mode 100644
index 406086da557b..000000000000
--- a/cmds/statsd/src/anomaly/AlarmTracker.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gtest/gtest_prod.h>
-
-#include "AlarmMonitor.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm
-
-#include <stdlib.h>
-#include <utils/RefBase.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class AlarmTracker : public virtual RefBase {
-public:
- AlarmTracker(const int64_t startMillis,
- const int64_t currentMillis,
- const Alarm& alarm, const ConfigKey& configKey,
- const sp<AlarmMonitor>& subscriberAlarmMonitor);
-
- virtual ~AlarmTracker();
-
- void onAlarmFired();
-
- void addSubscription(const Subscription& subscription);
-
- void informAlarmsFired(const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms);
-
-protected:
- // For test only. Returns the alarm timestamp in seconds. Otherwise returns 0.
- inline int32_t getAlarmTimestampSec() const {
- return mInternalAlarm == nullptr ? 0 : mInternalAlarm->timestampSec;
- }
-
- int64_t findNextAlarmSec(int64_t currentTimeMillis);
-
- // statsd_config.proto Alarm message that defines this tracker.
- const Alarm mAlarmConfig;
-
- // A reference to the Alarm's config key.
- const ConfigKey mConfigKey;
-
- // The subscriptions that depend on this alarm.
- std::vector<Subscription> mSubscriptions;
-
- // Alarm monitor.
- sp<AlarmMonitor> mAlarmMonitor;
-
- // The current expected alarm time in seconds.
- int64_t mAlarmSec;
-
- // The current alarm.
- sp<const InternalAlarm> mInternalAlarm;
-
- FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp);
- FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateAlarms);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
deleted file mode 100644
index 6aa410b180b8..000000000000
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ /dev/null
@@ -1,326 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "AnomalyTracker.h"
-#include "external/Perfetto.h"
-#include "guardrail/StatsdStats.h"
-#include "metadata_util.h"
-#include "stats_log_util.h"
-#include "subscriber_util.h"
-#include "subscriber/IncidentdReporter.h"
-#include "subscriber/SubscriberReporter.h"
-
-#include <inttypes.h>
-#include <statslog_statsd.h>
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
- : mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) {
- VLOG("AnomalyTracker() called");
- resetStorage(); // initialization
-}
-
-AnomalyTracker::~AnomalyTracker() {
- VLOG("~AnomalyTracker() called");
-}
-
-void AnomalyTracker::onConfigUpdated() {
- mSubscriptions.clear();
-}
-
-void AnomalyTracker::resetStorage() {
- VLOG("resetStorage() called.");
- mPastBuckets.clear();
- // Excludes the current bucket.
- mPastBuckets.resize(mNumOfPastBuckets);
- mSumOverPastBuckets.clear();
-}
-
-size_t AnomalyTracker::index(int64_t bucketNum) const {
- if (bucketNum < 0) {
- ALOGE("index() was passed a negative bucket number (%lld)!", (long long)bucketNum);
- }
- return bucketNum % mNumOfPastBuckets;
-}
-
-void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) {
- VLOG("advanceMostRecentBucketTo() called.");
- if (mNumOfPastBuckets <= 0) {
- return;
- }
- if (bucketNum <= mMostRecentBucketNum) {
- ALOGW("Cannot advance buckets backwards (bucketNum=%lld but mMostRecentBucketNum=%lld)",
- (long long)bucketNum, (long long)mMostRecentBucketNum);
- return;
- }
- // If in the future (i.e. buckets are ancient), just empty out all past info.
- if (bucketNum >= mMostRecentBucketNum + mNumOfPastBuckets) {
- resetStorage();
- mMostRecentBucketNum = bucketNum;
- return;
- }
-
- // Clear out space by emptying out old mPastBuckets[i] values and update mSumOverPastBuckets.
- for (int64_t i = mMostRecentBucketNum + 1; i <= bucketNum; i++) {
- const int idx = index(i);
- subtractBucketFromSum(mPastBuckets[idx]);
- mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
- }
- mMostRecentBucketNum = bucketNum;
-}
-
-void AnomalyTracker::addPastBucket(const MetricDimensionKey& key,
- const int64_t& bucketValue,
- const int64_t& bucketNum) {
- VLOG("addPastBucket(bucketValue) called.");
- if (mNumOfPastBuckets == 0 ||
- bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
- return;
- }
-
- const int bucketIndex = index(bucketNum);
- if (bucketNum <= mMostRecentBucketNum && (mPastBuckets[bucketIndex] != nullptr)) {
- // We need to insert into an already existing past bucket.
- std::shared_ptr<DimToValMap>& bucket = mPastBuckets[bucketIndex];
- auto itr = bucket->find(key);
- if (itr != bucket->end()) {
- // Old entry already exists; update it.
- subtractValueFromSum(key, itr->second);
- itr->second = bucketValue;
- } else {
- bucket->insert({key, bucketValue});
- }
- mSumOverPastBuckets[key] += bucketValue;
- } else {
- // Bucket does not exist yet (in future or was never made), so we must make it.
- std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
- bucket->insert({key, bucketValue});
- addPastBucket(bucket, bucketNum);
- }
-}
-
-void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucket,
- const int64_t& bucketNum) {
- VLOG("addPastBucket(bucket) called.");
- if (mNumOfPastBuckets == 0 ||
- bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
- return;
- }
-
- if (bucketNum <= mMostRecentBucketNum) {
- // We are updating an old bucket, not adding a new one.
- subtractBucketFromSum(mPastBuckets[index(bucketNum)]);
- } else {
- // Clear space for the new bucket to be at bucketNum.
- advanceMostRecentBucketTo(bucketNum);
- }
- mPastBuckets[index(bucketNum)] = bucket;
- addBucketToSum(bucket);
-}
-
-void AnomalyTracker::subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- for (const auto& keyValuePair : *bucket) {
- subtractValueFromSum(keyValuePair.first, keyValuePair.second);
- }
-}
-
-
-void AnomalyTracker::subtractValueFromSum(const MetricDimensionKey& key,
- const int64_t& bucketValue) {
- auto itr = mSumOverPastBuckets.find(key);
- if (itr == mSumOverPastBuckets.end()) {
- return;
- }
- itr->second -= bucketValue;
- if (itr->second == 0) {
- mSumOverPastBuckets.erase(itr);
- }
-}
-
-void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- // For each dimension present in the bucket, add its value to its corresponding sum.
- for (const auto& keyValuePair : *bucket) {
- mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
- }
-}
-
-int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
- const int64_t& bucketNum) const {
- if (bucketNum < 0 || mMostRecentBucketNum < 0
- || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
- || bucketNum > mMostRecentBucketNum) {
- return 0;
- }
-
- const auto& bucket = mPastBuckets[index(bucketNum)];
- if (bucket == nullptr) {
- return 0;
- }
- const auto& itr = bucket->find(key);
- return itr == bucket->end() ? 0 : itr->second;
-}
-
-int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
- const auto& itr = mSumOverPastBuckets.find(key);
- if (itr != mSumOverPastBuckets.end()) {
- return itr->second;
- }
- return 0;
-}
-
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
- const MetricDimensionKey& key,
- const int64_t& currentBucketValue) {
-
- // currentBucketNum should be the next bucket after pastBuckets. If not, advance so that it is.
- if (currentBucketNum > mMostRecentBucketNum + 1) {
- advanceMostRecentBucketTo(currentBucketNum - 1);
- }
- return mAlert.has_trigger_if_sum_gt() &&
- getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
-}
-
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
- const MetricDimensionKey& key, int64_t metricValue) {
- // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
- // real time right now.
- if (isInRefractoryPeriod(timestampNs, key)) {
- VLOG("Skipping anomaly declaration since within refractory period");
- return;
- }
- if (mAlert.has_refractory_period_secs()) {
- mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up
- + mAlert.refractory_period_secs();
- // TODO(b/110563466): If we had access to the bucket_size_millis, consider
- // calling resetStorage()
- // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) {resetStorage();}
- }
-
- if (!mSubscriptions.empty()) {
- ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.",
- mAlert.id(), key.toString().c_str());
- informSubscribers(key, metricId, metricValue);
- } else {
- ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
- }
-
- StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
-
- // TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
- util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(),
- mConfigKey.GetId(), mAlert.id());
-}
-
-void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
- const int64_t& currBucketNum, int64_t metricId,
- const MetricDimensionKey& key,
- const int64_t& currentBucketValue) {
- if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
- declareAnomaly(timestampNs, metricId, key, currentBucketValue);
- }
-}
-
-bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs,
- const MetricDimensionKey& key) const {
- const auto& it = mRefractoryPeriodEndsSec.find(key);
- if (it != mRefractoryPeriodEndsSec.end()) {
- return timestampNs < (it->second * (int64_t)NS_PER_SEC);
- }
- return false;
-}
-
-std::pair<bool, uint64_t> AnomalyTracker::getProtoHash() const {
- string serializedAlert;
- if (!mAlert.SerializeToString(&serializedAlert)) {
- ALOGW("Unable to serialize alert %lld", (long long)mAlert.id());
- return {false, 0};
- }
- return {true, Hash64(serializedAlert)};
-}
-
-void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
- int64_t metricValue) {
- triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
-}
-
-bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::AlertMetadata* alertMetadata) {
- bool metadataWritten = false;
-
- if (mRefractoryPeriodEndsSec.empty()) {
- return false;
- }
-
- for (const auto& it: mRefractoryPeriodEndsSec) {
- // Do not write the timestamp to disk if it has already expired
- if (it.second < systemElapsedTimeNs / NS_PER_SEC) {
- continue;
- }
-
- metadataWritten = true;
- if (alertMetadata->alert_dim_keyed_data_size() == 0) {
- alertMetadata->set_alert_id(mAlert.id());
- }
-
- metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data();
- // We convert and write the refractory_end_sec to wall clock time because we do not know
- // when statsd will start again.
- int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) +
- (it.second - systemElapsedTimeNs / NS_PER_SEC));
-
- keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec);
- writeMetricDimensionKeyToMetadataDimensionKey(
- it.first, keyedData->mutable_dimension_key());
- }
-
- return metadataWritten;
-}
-
-void AnomalyTracker::loadAlertMetadata(
- const metadata::AlertMetadata& alertMetadata,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs) {
- for (const metadata::AlertDimensionKeyedData& keyedData :
- alertMetadata.alert_dim_keyed_data()) {
- if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) {
- // Do not update the timestamp if it has already expired.
- continue;
- }
- MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto(
- keyedData.dimension_key());
- int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() -
- currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC;
- mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec;
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
deleted file mode 100644
index 9a578ee0696d..000000000000
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ /dev/null
@@ -1,229 +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.
- */
-
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include <stdlib.h>
-#include <utils/RefBase.h>
-
-#include "AlarmMonitor.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
-#include "hash.h"
-#include "stats_util.h" // HashableDimensionKey and DimToValMap
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::shared_ptr;
-using std::unordered_map;
-
-// Does NOT allow negative values.
-class AnomalyTracker : public virtual RefBase {
-public:
- AnomalyTracker(const Alert& alert, const ConfigKey& configKey);
-
- virtual ~AnomalyTracker();
-
- // Reset appropriate state on a config update. Clear subscriptions so they can be reset.
- void onConfigUpdated();
-
- // Add subscriptions that depend on this alert.
- void addSubscription(const Subscription& subscription) {
- mSubscriptions.push_back(subscription);
- }
-
- // Adds a bucket for the given bucketNum (index starting at 0).
- // If a bucket for bucketNum already exists, it will be replaced.
- // Also, advances to bucketNum (if not in the past), effectively filling any intervening
- // buckets with 0s.
- void addPastBucket(std::shared_ptr<DimToValMap> bucket, const int64_t& bucketNum);
-
- // Inserts (or replaces) the bucket entry for the given bucketNum at the given key to be the
- // given bucketValue. If the bucket does not exist, it will be created.
- // Also, advances to bucketNum (if not in the past), effectively filling any intervening
- // buckets with 0s.
- void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
- const int64_t& bucketNum);
-
- // Returns true if, based on past buckets plus the new currentBucketValue (which generally
- // represents the partially-filled current bucket), an anomaly has happened.
- // Also advances to currBucketNum-1.
- bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
- const int64_t& currentBucketValue);
-
- // Informs incidentd about the detected alert.
- void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
- int64_t metricValue);
-
- // Detects if, based on past buckets plus the new currentBucketValue (which generally
- // represents the partially-filled current bucket), an anomaly has happened, and if so,
- // declares an anomaly and informs relevant subscribers.
- // Also advances to currBucketNum-1.
- void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
- int64_t metricId, const MetricDimensionKey& key,
- const int64_t& currentBucketValue);
-
- // Init the AlarmMonitor which is shared across anomaly trackers.
- virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
- return; // Base AnomalyTracker class has no need for the AlarmMonitor.
- }
-
- // Returns the sum of all past bucket values for the given dimension key.
- int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
-
- // Returns the value for a past bucket, or 0 if that bucket doesn't exist.
- int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
-
- // Returns the anomaly threshold set in the configuration.
- inline int64_t getAnomalyThreshold() const {
- return mAlert.trigger_if_sum_gt();
- }
-
- // Returns the refractory period ending timestamp (in seconds) for the given key.
- // Before this moment, any detected anomaly will be ignored.
- // If there is no stored refractory period ending timestamp, returns 0.
- uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
- const auto& it = mRefractoryPeriodEndsSec.find(key);
- return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
- }
-
- // Returns the (constant) number of past buckets this anomaly tracker can store.
- inline int getNumOfPastBuckets() const {
- return mNumOfPastBuckets;
- }
-
- std::pair<bool, uint64_t> getProtoHash() const;
-
- // Sets an alarm for the given timestamp.
- // Replaces previous alarm if one already exists.
- virtual void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) {
- return; // The base AnomalyTracker class doesn't have alarms.
- }
-
- // Stops the alarm.
- // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
- // declare the anomaly now.
- virtual void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) {
- return; // The base AnomalyTracker class doesn't have alarms.
- }
-
- // Stop all the alarms owned by this tracker. Does not declare any anomalies.
- virtual void cancelAllAlarms() {
- return; // The base AnomalyTracker class doesn't have alarms.
- }
-
- // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
- // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
- virtual void informAlarmsFired(const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
- return; // The base AnomalyTracker class doesn't have alarms.
- }
-
- // Writes metadata of the alert (refractory_period_end_sec) to AlertMetadata.
- // Returns true if at least one element is written to alertMetadata.
- bool writeAlertMetadataToProto(
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs, metadata::AlertMetadata* alertMetadata);
-
- void loadAlertMetadata(
- const metadata::AlertMetadata& alertMetadata,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs);
-
-protected:
- // For testing only.
- // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
- // returns 0.
- virtual uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const {
- return 0; // The base AnomalyTracker class doesn't have alarms.
- }
-
- // statsd_config.proto Alert message that defines this tracker.
- const Alert mAlert;
-
- // The subscriptions that depend on this alert.
- std::vector<Subscription> mSubscriptions;
-
- // A reference to the Alert's config key.
- const ConfigKey mConfigKey;
-
- // Number of past buckets. One less than the total number of buckets needed
- // for the anomaly detection (since the current bucket is not in the past).
- const int mNumOfPastBuckets;
-
- // Values for each of the past mNumOfPastBuckets buckets. Always of size mNumOfPastBuckets.
- // mPastBuckets[i] can be null, meaning that no data is present in that bucket.
- std::vector<shared_ptr<DimToValMap>> mPastBuckets;
-
- // Cached sum over all existing buckets in mPastBuckets.
- // Its buckets never contain entries of 0.
- DimToValMap mSumOverPastBuckets;
-
- // The bucket number of the last added bucket.
- int64_t mMostRecentBucketNum = -1;
-
- // Map from each dimension to the timestamp that its refractory period (if this anomaly was
- // declared for that dimension) ends, in seconds. From this moment and onwards, anomalies
- // can be declared again.
- // Entries may be, but are not guaranteed to be, removed after the period is finished.
- unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
-
- // Advances mMostRecentBucketNum to bucketNum, deleting any data that is now too old.
- // Specifically, since it is now too old, removes the data for
- // [mMostRecentBucketNum - mNumOfPastBuckets + 1, bucketNum - mNumOfPastBuckets].
- void advanceMostRecentBucketTo(const int64_t& bucketNum);
-
- // Add the information in the given bucket to mSumOverPastBuckets.
- void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
-
- // Subtract the information in the given bucket from mSumOverPastBuckets
- // and remove any items with value 0.
- void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
-
- // From mSumOverPastBuckets[key], subtracts bucketValue, removing it if it is now 0.
- void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue);
-
- // Returns true if in the refractory period, else false.
- bool isInRefractoryPeriod(const int64_t& timestampNs, const MetricDimensionKey& key) const;
-
- // Calculates the corresponding bucket index within the circular array.
- // Requires bucketNum >= 0.
- size_t index(int64_t bucketNum) const;
-
- // Resets all bucket data. For use when all the data gets stale.
- virtual void resetStorage();
-
- // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred.
- void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue);
-
- FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
- FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
- FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
- FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
deleted file mode 100644
index 2b56810170e5..000000000000
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ /dev/null
@@ -1,115 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "DurationAnomalyTracker.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-DurationAnomalyTracker::DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
- const sp<AlarmMonitor>& alarmMonitor)
- : AnomalyTracker(alert, configKey), mAlarmMonitor(alarmMonitor) {
- VLOG("DurationAnomalyTracker() called");
-}
-
-DurationAnomalyTracker::~DurationAnomalyTracker() {
- VLOG("~DurationAnomalyTracker() called");
- cancelAllAlarms();
-}
-
-void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
- const int64_t& timestampNs) {
- // Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely.
- uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1) / NS_PER_SEC) + 1; // round up
- if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
- VLOG("Not setting anomaly alarm since it would fall in the refractory period.");
- return;
- }
-
- auto itr = mAlarms.find(dimensionKey);
- if (itr != mAlarms.end() && mAlarmMonitor != nullptr) {
- mAlarmMonitor->remove(itr->second);
- }
-
- sp<const InternalAlarm> alarm = new InternalAlarm{timestampSec};
- mAlarms[dimensionKey] = alarm;
- if (mAlarmMonitor != nullptr) {
- mAlarmMonitor->add(alarm);
- }
-}
-
-void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey,
- const int64_t& timestampNs) {
- const auto itr = mAlarms.find(dimensionKey);
- if (itr == mAlarms.end()) {
- return;
- }
-
- // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now.
- if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) {
- declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey,
- mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) -
- itr->second->timestampSec);
- }
- if (mAlarmMonitor != nullptr) {
- mAlarmMonitor->remove(itr->second);
- }
- mAlarms.erase(dimensionKey);
-}
-
-void DurationAnomalyTracker::cancelAllAlarms() {
- if (mAlarmMonitor != nullptr) {
- for (const auto& itr : mAlarms) {
- mAlarmMonitor->remove(itr.second);
- }
- }
- mAlarms.clear();
-}
-
-void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
-
- if (firedAlarms.empty() || mAlarms.empty()) return;
- // Find the intersection of firedAlarms and mAlarms.
- // The for loop is inefficient, since it loops over all keys, but that's okay since it is very
- // seldomly called. The alternative would be having InternalAlarms store information about the
- // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that
- // is rarely ever called.
- unordered_map<MetricDimensionKey, sp<const InternalAlarm>> matchedAlarms;
- for (const auto& kv : mAlarms) {
- if (firedAlarms.count(kv.second) > 0) {
- matchedAlarms.insert({kv.first, kv.second});
- }
- }
-
- // Now declare each of these alarms to have fired.
- for (const auto& kv : matchedAlarms) {
- declareAnomaly(
- timestampNs, mAlert.metric_id(), kv.first,
- mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec);
- mAlarms.erase(kv.first);
- firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it.
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
deleted file mode 100644
index 46419149580b..000000000000
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ /dev/null
@@ -1,79 +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.
- */
-
-#pragma once
-
-#include "AlarmMonitor.h"
-#include "AnomalyTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-
-class DurationAnomalyTracker : public virtual AnomalyTracker {
-public:
- DurationAnomalyTracker(const Alert& alert, const ConfigKey& configKey,
- const sp<AlarmMonitor>& alarmMonitor);
-
- virtual ~DurationAnomalyTracker();
-
- // Sets an alarm for the given timestamp.
- // Replaces previous alarm if one already exists.
- void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) override;
-
- // Stops the alarm.
- // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
- // declare the anomaly now.
- void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) override;
-
- // Stop all the alarms owned by this tracker. Does not declare any anomalies.
- void cancelAllAlarms() override;
-
- // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
- // and removes it from firedAlarms. The AlarmMonitor is not informed.
- // Note that this will generally be called from a different thread from the other functions;
- // the caller is responsible for thread safety.
- void informAlarmsFired(const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
-
-protected:
- // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
- // returns 0.
- uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const override {
- auto it = mAlarms.find(dimensionKey);
- return it == mAlarms.end() ? 0 : it->second->timestampSec;
- }
-
- // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
- // are still active.
- std::unordered_map<MetricDimensionKey, sp<const InternalAlarm>> mAlarms;
-
- // Anomaly alarm monitor.
- sp<AlarmMonitor> mAlarmMonitor;
-
- FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
- FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
- FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
- FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
- FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp);
- FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h
deleted file mode 100644
index 99882d0337b1..000000000000
--- a/cmds/statsd/src/anomaly/indexed_priority_queue.h
+++ /dev/null
@@ -1,224 +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.
- */
-
-#pragma once
-
-#include <utils/RefBase.h>
-#include <unordered_map>
-#include <vector>
-
-using namespace android;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/** Defines a hash function for sp<const AA>, returning the hash of the underlying pointer. */
-template <class AA>
-struct SpHash {
- size_t operator()(const sp<const AA>& k) const {
- return std::hash<const AA*>()(k.get());
- }
-};
-
-/**
- * Min priority queue for generic type AA.
- * Unlike a regular priority queue, this class is also capable of removing interior elements.
- * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning
- * whether a should be closer to the top of the queue than b.
- */
-template <class AA, class Comparator>
-class indexed_priority_queue {
-public:
- indexed_priority_queue();
- /** Adds a into the priority queue. If already present or a==nullptr, does nothing. */
- void push(sp<const AA> a);
- /*
- * Removes a from the priority queue. If not present or a==nullptr, does nothing.
- * Returns true if a had been present (and is now removed), else false.
- */
- bool remove(sp<const AA> a);
- /** Removes the top element, if there is one. */
- void pop();
- /** Removes all elements. */
- void clear();
- /** Returns whether priority queue contains a (not just a copy of a, but a itself). */
- bool contains(sp<const AA> a) const;
- /** Returns min element. Returns nullptr iff empty(). */
- sp<const AA> top() const;
- /** Returns number of elements in priority queue. */
- size_t size() const {
- return pq.size() - 1;
- } // pq is 1-indexed
- /** Returns true iff priority queue is empty. */
- bool empty() const {
- return size() < 1;
- }
-
-private:
- /** Vector representing a min-heap (1-indexed, with nullptr at 0). */
- std::vector<sp<const AA>> pq;
- /** Mapping of each element in pq to its index in pq (i.e. the inverse of a=pq[i]). */
- std::unordered_map<sp<const AA>, size_t, SpHash<AA>> indices;
-
- void init();
- void sift_up(size_t idx);
- void sift_down(size_t idx);
- /** Returns whether pq[idx1] is considered higher than pq[idx2], according to Comparator. */
- bool higher(size_t idx1, size_t idx2) const;
- void swap_indices(size_t i, size_t j);
-};
-
-// Implementation must be done in this file due to use of template.
-
-template <class AA, class Comparator>
-indexed_priority_queue<AA, Comparator>::indexed_priority_queue() {
- init();
-}
-
-template <class AA, class Comparator>
-void indexed_priority_queue<AA, Comparator>::push(sp<const AA> a) {
- if (a == nullptr) return;
- if (contains(a)) return;
- pq.push_back(a);
- size_t idx = size(); // index of last element since 1-indexed
- indices.insert({a, idx});
- sift_up(idx); // get the pq back in order
-}
-
-template <class AA, class Comparator>
-bool indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) {
- if (a == nullptr) return false;
- if (!contains(a)) return false;
- size_t idx = indices[a];
- if (idx >= pq.size()) {
- return false;
- }
- if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1)
- pq.pop_back();
- indices.erase(a);
- return true;
- }
- // move last element (guaranteed not to be at idx) to idx, then delete a
- sp<const AA> last_a = pq.back();
- pq[idx] = last_a;
- pq.pop_back();
- indices[last_a] = idx;
- indices.erase(a);
-
- // get the heap back in order (since the element at idx is not in order)
- sift_up(idx);
- sift_down(idx);
-
- return true;
-}
-
-// The same as, but slightly more efficient than, remove(top()).
-template <class AA, class Comparator>
-void indexed_priority_queue<AA, Comparator>::pop() {
- sp<const AA> a = top();
- if (a == nullptr) return;
- const size_t idx = 1;
- if (idx == size()) { // if a is the last element
- pq.pop_back();
- indices.erase(a);
- return;
- }
- // move last element (guaranteed not to be at idx) to idx, then delete a
- sp<const AA> last_a = pq.back();
- pq[idx] = last_a;
- pq.pop_back();
- indices[last_a] = idx;
- indices.erase(a);
-
- // get the heap back in order (since the element at idx is not in order)
- sift_down(idx);
-}
-
-template <class AA, class Comparator>
-void indexed_priority_queue<AA, Comparator>::clear() {
- pq.clear();
- indices.clear();
- init();
-}
-
-template <class AA, class Comparator>
-sp<const AA> indexed_priority_queue<AA, Comparator>::top() const {
- if (empty()) return nullptr;
- return pq[1];
-}
-
-template <class AA, class Comparator>
-void indexed_priority_queue<AA, Comparator>::init() {
- pq.push_back(nullptr); // so that pq is 1-indexed.
- indices.insert({nullptr, 0}); // just to be consistent with pq.
-}
-
-template <class AA, class Comparator>
-void indexed_priority_queue<AA, Comparator>::sift_up(size_t idx) {
- while (idx > 1) {
- size_t parent = idx / 2;
- if (higher(idx, parent))
- swap_indices(idx, parent);
- else
- break;
- idx = parent;
- }
-}
-
-template <class AA, class Comparator>
-void indexed_priority_queue<AA, Comparator>::sift_down(size_t idx) {
- while (2 * idx <= size()) {
- size_t child = 2 * idx;
- if (child < size() && higher(child + 1, child)) child++;
- if (higher(child, idx))
- swap_indices(child, idx);
- else
- break;
- idx = child;
- }
-}
-
-template <class AA, class Comparator>
-bool indexed_priority_queue<AA, Comparator>::higher(size_t idx1, size_t idx2) const {
- if (!(0u < idx1 && idx1 < pq.size() && 0u < idx2 && idx2 < pq.size())) {
- return false; // got to do something.
- }
- return Comparator()(pq[idx1], pq[idx2]);
-}
-
-template <class AA, class Comparator>
-bool indexed_priority_queue<AA, Comparator>::contains(sp<const AA> a) const {
- if (a == nullptr) return false; // publicly, we pretend that nullptr is not actually in pq.
- return indices.count(a) > 0;
-}
-
-template <class AA, class Comparator>
-void indexed_priority_queue<AA, Comparator>::swap_indices(size_t i, size_t j) {
- if (!(0u < i && i < pq.size() && 0u < j && j < pq.size())) {
- return;
- }
- sp<const AA> val_i = pq[i];
- sp<const AA> val_j = pq[j];
- pq[i] = val_j;
- pq[j] = val_i;
- indices[val_i] = j;
- indices[val_j] = i;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
deleted file mode 100644
index 5a4a41d01de6..000000000000
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "external/Perfetto.h"
-#include "subscriber/IncidentdReporter.h"
-#include "subscriber/SubscriberReporter.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey,
- int64_t metricValue, const ConfigKey& configKey,
- const std::vector<Subscription>& subscriptions) {
- VLOG("informSubscribers called.");
- if (subscriptions.empty()) {
- VLOG("No Subscriptions were associated.");
- return;
- }
-
- for (const Subscription& subscription : subscriptions) {
- if (subscription.probability_of_informing() < 1
- && ((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.");
- continue;
- }
- switch (subscription.subscriber_information_case()) {
- case Subscription::SubscriberInformationCase::kIncidentdDetails:
- if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId,
- dimensionKey, metricValue, configKey)) {
- ALOGW("Failed to generate incident report.");
- }
- break;
- case Subscription::SubscriberInformationCase::kPerfettoDetails:
- if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
- subscription.id(), ruleId, configKey)) {
- ALOGW("Failed to generate perfetto traces.");
- }
- break;
- case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
- SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription,
- dimensionKey);
- break;
- default:
- break;
- }
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h
deleted file mode 100644
index 1df3c8991f94..000000000000
--- a/cmds/statsd/src/anomaly/subscriber_util.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "config/ConfigKey.h"
-#include "HashableDimensionKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
- const MetricDimensionKey& dimensionKey, int64_t metricValue,
- const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
deleted file mode 100644
index 4574b2e34547..000000000000
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ /dev/null
@@ -1,244 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-#include "CombinationConditionTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-using std::vector;
-
-CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index,
- const uint64_t protoHash)
- : ConditionTracker(id, index, protoHash) {
- VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
-}
-
-CombinationConditionTracker::~CombinationConditionTracker() {
- VLOG("~CombinationConditionTracker() %lld", (long long)mConditionId);
-}
-
-bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack,
- vector<ConditionState>& conditionCache) {
- VLOG("Combination predicate init() %lld", (long long)mConditionId);
- if (mInitialized) {
- // All the children are guaranteed to be initialized, but the recursion is needed to
- // fill the conditionCache properly, since another combination condition or metric
- // might rely on this. The recursion is needed to compute the current condition.
-
- // Init is called instead of isConditionMet so that the ConditionKey can be filled with the
- // default key for sliced conditions, since we do not know all indirect descendants here.
- for (const int childIndex : mChildren) {
- if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
- allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers,
- conditionIdIndexMap, stack, conditionCache);
- }
- }
- conditionCache[mIndex] =
- evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
- return true;
- }
-
- // mark this node as visited in the recursion stack.
- stack[mIndex] = true;
-
- Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination();
-
- if (!combinationCondition.has_operation()) {
- return false;
- }
- mLogicalOperation = combinationCondition.operation();
-
- if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
- return false;
- }
-
- for (auto child : combinationCondition.predicate()) {
- auto it = conditionIdIndexMap.find(child);
-
- if (it == conditionIdIndexMap.end()) {
- ALOGW("Predicate %lld not found in the config", (long long)child);
- return false;
- }
-
- int childIndex = it->second;
- const auto& childTracker = allConditionTrackers[childIndex];
- // if the child is a visited node in the recursion -> circle detected.
- if (stack[childIndex]) {
- ALOGW("Circle detected!!!");
- return false;
- }
-
- bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
- conditionIdIndexMap, stack, conditionCache);
-
- if (!initChildSucceeded) {
- ALOGW("Child initialization failed %lld ", (long long)child);
- return false;
- } else {
- VLOG("Child initialization success %lld ", (long long)child);
- }
-
- if (allConditionTrackers[childIndex]->isSliced()) {
- setSliced(true);
- mSlicedChildren.push_back(childIndex);
- } else {
- mUnSlicedChildren.push_back(childIndex);
- }
- mChildren.push_back(childIndex);
- mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
- childTracker->getAtomMatchingTrackerIndex().end());
- }
-
- mUnSlicedPartCondition =
- evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache);
- conditionCache[mIndex] =
- evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
-
- // unmark this node in the recursion stack.
- stack[mIndex] = false;
-
- mInitialized = true;
-
- return true;
-}
-
-bool CombinationConditionTracker::onConfigUpdated(
- const vector<Predicate>& allConditionProtos, const int index,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const unordered_map<int64_t, int>& conditionTrackerMap) {
- ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
- atomMatchingTrackerMap, conditionTrackerMap);
- mTrackerIndex.clear();
- mChildren.clear();
- mUnSlicedChildren.clear();
- mSlicedChildren.clear();
- Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination();
-
- for (const int64_t child : combinationCondition.predicate()) {
- const auto& it = conditionTrackerMap.find(child);
-
- if (it == conditionTrackerMap.end()) {
- ALOGW("Predicate %lld not found in the config", (long long)child);
- return false;
- }
-
- int childIndex = it->second;
- const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex];
-
- // Ensures that the child's tracker indices are updated.
- if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers,
- atomMatchingTrackerMap, conditionTrackerMap)) {
- ALOGW("Child update failed %lld ", (long long)child);
- return false;
- }
-
- if (allConditionTrackers[childIndex]->isSliced()) {
- mSlicedChildren.push_back(childIndex);
- } else {
- mUnSlicedChildren.push_back(childIndex);
- }
- mChildren.push_back(childIndex);
- mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
- childTracker->getAtomMatchingTrackerIndex().end());
- }
- return true;
-}
-
-void CombinationConditionTracker::isConditionMet(
- const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const bool isPartialLink,
- vector<ConditionState>& conditionCache) const {
- // So far, this is fine as there is at most one child having sliced output.
- for (const int childIndex : mChildren) {
- if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
- allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
- isPartialLink,
- conditionCache);
- }
- }
- conditionCache[mIndex] =
- evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
-}
-
-void CombinationConditionTracker::evaluateCondition(
- const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
- const std::vector<sp<ConditionTracker>>& mAllConditions,
- std::vector<ConditionState>& nonSlicedConditionCache,
- std::vector<bool>& conditionChangedCache) {
- // value is up to date.
- if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
- return;
- }
-
- for (const int childIndex : mChildren) {
- // So far, this is fine as there is at most one child having sliced output.
- if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
- const sp<ConditionTracker>& child = mAllConditions[childIndex];
- child->evaluateCondition(event, eventMatcherValues, mAllConditions,
- nonSlicedConditionCache, conditionChangedCache);
- }
- }
-
- ConditionState newCondition =
- evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
- if (!mSliced) {
- bool nonSlicedChanged = (mUnSlicedPartCondition != newCondition);
- mUnSlicedPartCondition = newCondition;
-
- nonSlicedConditionCache[mIndex] = mUnSlicedPartCondition;
- conditionChangedCache[mIndex] = nonSlicedChanged;
- } else {
- mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
- nonSlicedConditionCache);
-
- for (const int childIndex : mChildren) {
- // If any of the sliced condition in children condition changes, the combination
- // condition may be changed too.
- if (conditionChangedCache[childIndex]) {
- conditionChangedCache[mIndex] = true;
- break;
- }
- }
- nonSlicedConditionCache[mIndex] = newCondition;
- VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
- conditionChangedCache[mIndex] == true);
- }
-}
-
-bool CombinationConditionTracker::equalOutputDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensions) const {
- if (mSlicedChildren.size() != 1 ||
- mSlicedChildren.front() >= (int)allConditions.size() ||
- mLogicalOperation != LogicalOperation::AND) {
- return false;
- }
- const sp<ConditionTracker>& slicedChild = allConditions.at(mSlicedChildren.front());
- return slicedChild->equalOutputDimensions(allConditions, dimensions);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
deleted file mode 100644
index 672d61c82268..000000000000
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ /dev/null
@@ -1,117 +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.
- */
-
-#ifndef COMBINATION_CONDITION_TRACKER_H
-#define COMBINATION_CONDITION_TRACKER_H
-
-#include "ConditionTracker.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class CombinationConditionTracker : public ConditionTracker {
-public:
- CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash);
-
- ~CombinationConditionTracker();
-
- bool init(const std::vector<Predicate>& allConditionConfig,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& conditionCache) override;
-
- bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
-
- void evaluateCondition(const LogEvent& event,
- const std::vector<MatchingState>& eventMatcherValues,
- const std::vector<sp<ConditionTracker>>& mAllConditions,
- std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache) override;
-
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- const bool isPartialLink,
- std::vector<ConditionState>& conditionCache) const override;
-
- // Only one child predicate can have dimension.
- const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const override {
- for (const auto& child : mChildren) {
- auto result = allConditions[child]->getChangedToTrueDimensions(allConditions);
- if (result != nullptr) {
- return result;
- }
- }
- return nullptr;
- }
-
- // Only one child predicate can have dimension.
- const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const override {
- for (const auto& child : mChildren) {
- auto result = allConditions[child]->getChangedToFalseDimensions(allConditions);
- if (result != nullptr) {
- return result;
- }
- }
- return nullptr;
- }
-
- bool IsSimpleCondition() const override { return false; }
-
- bool IsChangedDimensionTrackable() const override {
- return mLogicalOperation == LogicalOperation::AND && mSlicedChildren.size() == 1;
- }
-
- bool equalOutputDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensions) const override;
-
- void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const override {
- if (mSlicedChildren.size() == 1) {
- return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions(
- allConditions, dimensions);
- }
- }
-
-
-private:
- LogicalOperation mLogicalOperation;
-
- // Store index of the children Predicates.
- // We don't store string name of the Children, because we want to get rid of the hash map to
- // map the name to object. We don't want to store smart pointers to children, because it
- // increases the risk of circular dependency and memory leak.
- std::vector<int> mChildren;
-
- std::vector<int> mSlicedChildren;
- std::vector<int> mUnSlicedChildren;
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // COMBINATION_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h
deleted file mode 100644
index 1fbe25279736..000000000000
--- a/cmds/statsd/src/condition/ConditionTimer.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include <stdint.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * A simple stopwatch to time the duration of condition being true.
- *
- * The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition
- * changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps
- * should be elapsedRealTime in nano seconds.
- *
- * Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is
- * responsible for thread safety.
- */
-class ConditionTimer {
-public:
- explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
- if (initCondition) {
- mLastConditionChangeTimestampNs = bucketStartNs;
- }
- };
-
- // Tracks how long the condition has been stayed true in the *current* bucket.
- // When a new bucket is created, this value will be reset to 0.
- int64_t mTimerNs = 0;
-
- // Last elapsed real timestamp when condition changed.
- int64_t mLastConditionChangeTimestampNs = 0;
-
- bool mCondition = false;
-
- int64_t newBucketStart(int64_t nextBucketStartNs) {
- if (mCondition) {
- // Normally, the next bucket happens after the last condition
- // change. In this case, add the time between the condition becoming
- // true to the next bucket start time.
- // Otherwise, the next bucket start time is before the last
- // condition change time, this means that the condition was false at
- // the bucket boundary before the condition became true, so the
- // timer should not get updated and the last condition change time
- // remains as is.
- if (nextBucketStartNs >= mLastConditionChangeTimestampNs) {
- mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs);
- mLastConditionChangeTimestampNs = nextBucketStartNs;
- }
- } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) {
- // The next bucket start time is before the last condition change
- // time, this means that the condition was true at the bucket
- // boundary before the condition became false, so adjust the timer
- // to match how long the condition was true to the bucket boundary.
- // This means remove the amount the condition stayed true in the
- // next bucket from the current bucket.
- mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs);
- }
-
- int64_t temp = mTimerNs;
- mTimerNs = 0;
-
- if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) {
- // The next bucket start time is before the last condition change
- // time, this means that the condition was true at the bucket
- // boundary and remained true in the next bucket up to the condition
- // change to false, so adjust the timer to match how long the
- // condition stayed true in the next bucket (now the current bucket).
- mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs;
- }
- return temp;
- }
-
- void onConditionChanged(bool newCondition, int64_t timestampNs) {
- if (newCondition == mCondition) {
- return;
- }
- mCondition = newCondition;
- if (newCondition == false) {
- mTimerNs += (timestampNs - mLastConditionChangeTimestampNs);
- }
- mLastConditionChangeTimestampNs = timestampNs;
- }
-
- FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
- FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
deleted file mode 100644
index 5a6b8cf334eb..000000000000
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ /dev/null
@@ -1,187 +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.
- */
-
-#pragma once
-
-#include "condition/condition_util.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "matchers/AtomMatchingTracker.h"
-#include "matchers/matcher_util.h"
-
-#include <utils/RefBase.h>
-
-#include <unordered_map>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class ConditionTracker : public virtual RefBase {
-public:
- ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash)
- : mConditionId(id),
- mIndex(index),
- mInitialized(false),
- mTrackerIndex(),
- mUnSlicedPartCondition(ConditionState::kUnknown),
- mSliced(false),
- mProtoHash(protoHash){};
-
- virtual ~ConditionTracker(){};
-
- // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
- // be done in the constructor, but we do it separately because (1) easy to return a bool to
- // indicate whether the initialization is successful. (2) makes unit test easier.
- // This function can also be called on config updates, in which case it does nothing other than
- // fill the condition cache with the current condition.
- // allConditionConfig: the list of all Predicate config from statsd_config.
- // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
- // need to call init() on child conditions)
- // conditionIdIndexMap: the mapping from condition id to its index.
- // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
- // conditionCache: tracks initial conditions of all ConditionTrackers. returns the
- // current condition if called on a config update.
- virtual bool init(const std::vector<Predicate>& allConditionConfig,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap,
- std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0;
-
- // Update appropriate state on config updates. Primarily, all indices need to be updated.
- // This predicate and all of its children are guaranteed to be preserved across the update.
- // This function is recursive and will call onConfigUpdated on child conditions. It does not
- // manage cycle detection since all preserved conditions should not have any cycles.
- //
- // allConditionProtos: the new predicates.
- // index: the new index of this tracker in allConditionProtos and allConditionTrackers.
- // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
- // need to call onConfigUpdated() on child conditions)
- // atomMatchingTrackerMap: map of atom matcher id to index after the config update.
- // conditionTrackerMap: map of condition tracker id to index after the config update.
- // returns whether or not the update is successful.
- virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& conditionTrackerMap) {
- mIndex = index;
- return true;
- }
-
- // evaluate current condition given the new event.
- // event: the new log event
- // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always
- // process event before ConditionTrackers, because ConditionTracker depends
- // on AtomMatchingTrackers.
- // mAllConditions: the list of all ConditionTracker
- // conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
- // conditionChanged: the bit map to record whether the condition has changed.
- // If the condition has dimension, then any sub condition changes will report
- // conditionChanged.
- virtual void evaluateCondition(const LogEvent& event,
- const std::vector<MatchingState>& eventMatcherValues,
- const std::vector<sp<ConditionTracker>>& mAllConditions,
- std::vector<ConditionState>& conditionCache,
- std::vector<bool>& conditionChanged) = 0;
-
- // Query the condition with parameters.
- // [conditionParameters]: a map from condition name to the HashableDimensionKey to query the
- // condition.
- // [allConditions]: all condition trackers. This is needed because the condition evaluation is
- // done recursively
- // [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields
- // in the condition tracker output dimension.
- // [conditionCache]: the cache holding the condition evaluation values.
- virtual void isConditionMet(
- const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- const bool isPartialLink,
- std::vector<ConditionState>& conditionCache) const = 0;
-
- // return the list of AtomMatchingTracker index that this ConditionTracker uses.
- virtual const std::set<int>& getAtomMatchingTrackerIndex() const {
- return mTrackerIndex;
- }
-
- virtual void setSliced(bool sliced) {
- mSliced = mSliced | sliced;
- }
-
- inline bool isSliced() const {
- return mSliced;
- }
-
- virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
- virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
-
- inline int64_t getConditionId() const {
- return mConditionId;
- }
-
- inline uint64_t getProtoHash() const {
- return mProtoHash;
- }
-
- virtual void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const = 0;
-
- virtual bool IsChangedDimensionTrackable() const = 0;
-
- virtual bool IsSimpleCondition() const = 0;
-
- virtual bool equalOutputDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensions) const = 0;
-
- // Return the current condition state of the unsliced part of the condition.
- inline ConditionState getUnSlicedPartConditionState() const {
- return mUnSlicedPartCondition;
- }
-
-protected:
- const int64_t mConditionId;
-
- // the index of this condition in the manager's condition list.
- int mIndex;
-
- // if it's properly initialized.
- bool mInitialized;
-
- // the list of AtomMatchingTracker index that this ConditionTracker uses.
- std::set<int> mTrackerIndex;
-
- // This variable is only used for CombinationConditionTrackers.
- // SimpleConditionTrackers technically don't have an unsliced part because
- // they are either sliced or unsliced.
- //
- // CombinationConditionTrackers have multiple children ConditionTrackers
- // that can be a mixture of sliced or unsliced. This tracks the
- // condition of the unsliced part of the combination condition.
- ConditionState mUnSlicedPartCondition;
-
- bool mSliced;
-
- // Hash of the Predicate's proto bytes from StatsdConfig.
- // Used to determine if the definition of this condition has changed across a config update.
- const uint64_t mProtoHash;
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
deleted file mode 100644
index c542032b48ea..000000000000
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ /dev/null
@@ -1,70 +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.
- */
-#include "ConditionWizard.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters,
- const bool isPartialLink) {
- vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
-
- mAllConditions[index]->isConditionMet(
- parameters, mAllConditions, isPartialLink,
- cache);
- return cache[index];
-}
-
-const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
- const int index) const {
- return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions);
-}
-
-const set<HashableDimensionKey>* ConditionWizard::getChangedToFalseDimensions(
- const int index) const {
- return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions);
-}
-
-bool ConditionWizard::IsChangedDimensionTrackable(const int index) {
- if (index >= 0 && index < (int)mAllConditions.size()) {
- return mAllConditions[index]->IsChangedDimensionTrackable();
- } else {
- return false;
- }
-}
-
-bool ConditionWizard::IsSimpleCondition(const int index) {
- if (index >= 0 && index < (int)mAllConditions.size()) {
- return mAllConditions[index]->IsSimpleCondition();
- } else {
- return false;
- }
-}
-
-bool ConditionWizard::equalOutputDimensions(const int index, const vector<Matcher>& dimensions) {
- if (index >= 0 && index < (int)mAllConditions.size()) {
- return mAllConditions[index]->equalOutputDimensions(mAllConditions, dimensions);
- } else {
- return false;
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
deleted file mode 100644
index 892647910d9f..000000000000
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ /dev/null
@@ -1,68 +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.
- */
-
-#ifndef CONDITION_WIZARD_H
-#define CONDITION_WIZARD_H
-
-#include "ConditionTracker.h"
-#include "condition_util.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Held by MetricProducer, to query a condition state with input defined in MetricConditionLink.
-class ConditionWizard : public virtual android::RefBase {
-public:
- ConditionWizard(){}; // for testing
- explicit ConditionWizard(std::vector<sp<ConditionTracker>>& conditionTrackers)
- : mAllConditions(conditionTrackers){};
-
- virtual ~ConditionWizard(){};
-
- // Query condition state, for a ConditionTracker at [conditionIndex], with [conditionParameters]
- // [conditionParameters] mapping from condition name to the HashableDimensionKey to query the
- // condition.
- // The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
- // the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
- virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters,
- const bool isPartialLink);
-
- virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const;
- virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
- const int index) const;
- bool equalOutputDimensions(const int index, const vector<Matcher>& dimensions);
-
- bool IsChangedDimensionTrackable(const int index);
- bool IsSimpleCondition(const int index);
-
- ConditionState getUnSlicedPartConditionState(const int index) {
- return mAllConditions[index]->getUnSlicedPartConditionState();
- }
- void getTrueSlicedDimensions(const int index,
- std::set<HashableDimensionKey>* trueDimensions) const {
- return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions);
- }
-
-private:
- std::vector<sp<ConditionTracker>> mAllConditions;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // CONDITION_WIZARD_H
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
deleted file mode 100644
index 1dcc8f96131a..000000000000
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ /dev/null
@@ -1,409 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "SimpleConditionTracker.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-
-SimpleConditionTracker::SimpleConditionTracker(
- const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index,
- const SimplePredicate& simplePredicate,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap)
- : ConditionTracker(id, index, protoHash),
- mConfigKey(key),
- mContainANYPositionInInternalDimensions(false) {
- VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
- mCountNesting = simplePredicate.count_nesting();
-
- setMatcherIndices(simplePredicate, atomMatchingTrackerMap);
-
- if (simplePredicate.has_dimensions()) {
- translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
- if (mOutputDimensions.size() > 0) {
- mSliced = true;
- }
- mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
- }
-
- if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
- mInitialValue = ConditionState::kFalse;
- } else {
- mInitialValue = ConditionState::kUnknown;
- }
-
- mInitialized = true;
-}
-
-SimpleConditionTracker::~SimpleConditionTracker() {
- VLOG("~SimpleConditionTracker()");
-}
-
-bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack, vector<ConditionState>& conditionCache) {
- // SimpleConditionTracker does not have dependency on other conditions, thus we just return
- // if the initialization was successful.
- ConditionKey conditionKey;
- if (mSliced) {
- conditionKey[mConditionId] = DEFAULT_DIMENSION_KEY;
- }
- isConditionMet(conditionKey, allConditionTrackers, mSliced, conditionCache);
- return mInitialized;
-}
-
-bool SimpleConditionTracker::onConfigUpdated(
- const vector<Predicate>& allConditionProtos, const int index,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const unordered_map<int64_t, int>& conditionTrackerMap) {
- ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
- atomMatchingTrackerMap, conditionTrackerMap);
- setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap);
- return true;
-}
-
-void SimpleConditionTracker::setMatcherIndices(
- const SimplePredicate& simplePredicate,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
- mTrackerIndex.clear();
- if (simplePredicate.has_start()) {
- auto pair = atomMatchingTrackerMap.find(simplePredicate.start());
- if (pair == atomMatchingTrackerMap.end()) {
- ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
- return;
- }
- mStartLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStartLogMatcherIndex);
- } else {
- mStartLogMatcherIndex = -1;
- }
-
- if (simplePredicate.has_stop()) {
- auto pair = atomMatchingTrackerMap.find(simplePredicate.stop());
- if (pair == atomMatchingTrackerMap.end()) {
- ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
- return;
- }
- mStopLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStopLogMatcherIndex);
- } else {
- mStopLogMatcherIndex = -1;
- }
-
- if (simplePredicate.has_stop_all()) {
- auto pair = atomMatchingTrackerMap.find(simplePredicate.stop_all());
- if (pair == atomMatchingTrackerMap.end()) {
- ALOGW("Stop all matcher %lld found in the config",
- (long long)simplePredicate.stop_all());
- return;
- }
- mStopAllLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStopAllLogMatcherIndex);
- } else {
- mStopAllLogMatcherIndex = -1;
- }
-}
-
-void SimpleConditionTracker::dumpState() {
- VLOG("%lld DUMP:", (long long)mConditionId);
- for (const auto& pair : mSlicedConditionState) {
- VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second);
- }
-
- VLOG("Changed to true keys: \n");
- for (const auto& key : mLastChangedToTrueDimensions) {
- VLOG("%s", key.toString().c_str());
- }
- VLOG("Changed to false keys: \n");
- for (const auto& key : mLastChangedToFalseDimensions) {
- VLOG("%s", key.toString().c_str());
- }
-}
-
-void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
- std::vector<bool>& conditionChangedCache) {
- // Unless the default condition is false, and there was nothing started, otherwise we have
- // triggered a condition change.
- conditionChangedCache[mIndex] =
- (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
- : true;
-
- for (const auto& cond : mSlicedConditionState) {
- if (cond.second > 0) {
- mLastChangedToFalseDimensions.insert(cond.first);
- }
- }
-
- // After StopAll, we know everything has stopped. From now on, default condition is false.
- mInitialValue = ConditionState::kFalse;
- mSlicedConditionState.clear();
- conditionCache[mIndex] = ConditionState::kFalse;
-}
-
-bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
- if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
- // if the condition is not sliced or the key is not new, we are good!
- return false;
- }
- // 1. Report the tuple count if the tuple count > soft limit
- if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mSlicedConditionState.size() + 1;
- StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("Predicate %lld dropping data for dimension key %s",
- (long long)mConditionId, newKey.toString().c_str());
- return true;
- }
- }
- return false;
-}
-
-void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
- bool matchStart, ConditionState* conditionCache,
- bool* conditionChangedCache) {
- bool changed = false;
- auto outputIt = mSlicedConditionState.find(outputKey);
- ConditionState newCondition;
- if (hitGuardRail(outputKey)) {
- (*conditionChangedCache) = false;
- // Tells the caller it's evaluated.
- (*conditionCache) = ConditionState::kUnknown;
- return;
- }
- if (outputIt == mSlicedConditionState.end()) {
- // We get a new output key.
- newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
- if (matchStart && mInitialValue != ConditionState::kTrue) {
- mSlicedConditionState[outputKey] = 1;
- changed = true;
- mLastChangedToTrueDimensions.insert(outputKey);
- } else if (mInitialValue != ConditionState::kFalse) {
- // it's a stop and we don't have history about it.
- // If the default condition is not false, it means this stop is valuable to us.
- mSlicedConditionState[outputKey] = 0;
- mLastChangedToFalseDimensions.insert(outputKey);
- changed = true;
- }
- } else {
- // we have history about this output key.
- auto& startedCount = outputIt->second;
- // assign the old value first.
- newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- if (matchStart) {
- if (startedCount == 0) {
- mLastChangedToTrueDimensions.insert(outputKey);
- // This condition for this output key will change from false -> true
- changed = true;
- }
-
- // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
- // as 1 if not counting nesting.
- startedCount++;
- newCondition = ConditionState::kTrue;
- } else {
- // This is a stop event.
- if (startedCount > 0) {
- if (mCountNesting) {
- startedCount--;
- if (startedCount == 0) {
- newCondition = ConditionState::kFalse;
- }
- } else {
- // not counting nesting, so ignore the number of starts, stop now.
- startedCount = 0;
- newCondition = ConditionState::kFalse;
- }
- // if everything has stopped for this output key, condition true -> false;
- if (startedCount == 0) {
- mLastChangedToFalseDimensions.insert(outputKey);
- changed = true;
- }
- }
-
- // if default condition is false, it means we don't need to keep the false values.
- if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
- mSlicedConditionState.erase(outputIt);
- VLOG("erase key %s", outputKey.toString().c_str());
- }
- }
- }
-
- // dump all dimensions for debugging
- if (DEBUG) {
- dumpState();
- }
-
- (*conditionChangedCache) = changed;
- (*conditionCache) = newCondition;
-
- VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
- conditionChangedCache[mIndex] == true);
-}
-
-void SimpleConditionTracker::evaluateCondition(
- const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d",
- (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
- mLastChangedToTrueDimensions.clear();
- mLastChangedToFalseDimensions.clear();
-
- if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
- eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
- handleStopAll(conditionCache, conditionChangedCache);
- return;
- }
-
- int matchedState = -1;
- // Note: The order to evaluate the following start, stop, stop_all matters.
- // The priority of overwrite is stop_all > stop > start.
- if (mStartLogMatcherIndex >= 0 &&
- eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
- matchedState = 1;
- }
-
- if (mStopLogMatcherIndex >= 0 &&
- eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
- matchedState = 0;
- }
-
- if (matchedState < 0) {
- // The event doesn't match this condition. So we just report existing condition values.
- conditionChangedCache[mIndex] = false;
- if (mSliced) {
- // if the condition result is sliced. The overall condition is true if any of the sliced
- // condition is true
- conditionCache[mIndex] = mInitialValue;
- for (const auto& slicedCondition : mSlicedConditionState) {
- if (slicedCondition.second > 0) {
- conditionCache[mIndex] = ConditionState::kTrue;
- break;
- }
- }
- } else {
- const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
- if (itr == mSlicedConditionState.end()) {
- // condition not sliced, but we haven't seen the matched start or stop yet. so
- // return initial value.
- conditionCache[mIndex] = mInitialValue;
- } else {
- // return the cached condition.
- conditionCache[mIndex] =
- itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- }
- }
- return;
- }
-
- ConditionState overallState = mInitialValue;
- bool overallChanged = false;
-
- if (mOutputDimensions.size() == 0) {
- handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState,
- &overallChanged);
- } else if (!mContainANYPositionInInternalDimensions) {
- HashableDimensionKey outputValue;
- filterValues(mOutputDimensions, event.getValues(), &outputValue);
-
- // If this event has multiple nodes in the attribution chain, this log event probably will
- // generate multiple dimensions. If so, we will find if the condition changes for any
- // dimension and ask the corresponding metric producer to verify whether the actual sliced
- // condition has changed or not.
- // A high level assumption is that a predicate is either sliced or unsliced. We will never
- // have both sliced and unsliced version of a predicate.
- handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged);
- } else {
- ALOGE("The condition tracker should not be sliced by ANY position matcher.");
- }
- conditionCache[mIndex] = overallState;
- conditionChangedCache[mIndex] = overallChanged;
-}
-
-void SimpleConditionTracker::isConditionMet(
- const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const bool isPartialLink,
- vector<ConditionState>& conditionCache) const {
-
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d",
- (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
- const auto pair = conditionParameters.find(mConditionId);
-
- if (pair == conditionParameters.end()) {
- ConditionState conditionState = ConditionState::kNotEvaluated;
- conditionState = conditionState | mInitialValue;
- if (!mSliced) {
- const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
- if (itr != mSlicedConditionState.end()) {
- ConditionState sliceState =
- itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
- }
- }
- conditionCache[mIndex] = conditionState;
- return;
- }
-
- ConditionState conditionState = ConditionState::kNotEvaluated;
- const HashableDimensionKey& key = pair->second;
- if (isPartialLink) {
- // For unseen key, check whether the require dimensions are subset of sliced condition
- // output.
- conditionState = conditionState | mInitialValue;
- for (const auto& slice : mSlicedConditionState) {
- ConditionState sliceState =
- slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- if (slice.first.contains(key)) {
- conditionState = conditionState | sliceState;
- }
- }
- } else {
- auto startedCountIt = mSlicedConditionState.find(key);
- conditionState = conditionState | mInitialValue;
- if (startedCountIt != mSlicedConditionState.end()) {
- ConditionState sliceState =
- startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
- }
-
- }
- conditionCache[mIndex] = conditionState;
- VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
deleted file mode 100644
index 7a8b40108448..000000000000
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ /dev/null
@@ -1,145 +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.
- */
-
-#ifndef SIMPLE_CONDITION_TRACKER_H
-#define SIMPLE_CONDITION_TRACKER_H
-
-#include <gtest/gtest_prod.h>
-#include "ConditionTracker.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class SimpleConditionTracker : public ConditionTracker {
-public:
- SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash,
- const int index, const SimplePredicate& simplePredicate,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap);
-
- ~SimpleConditionTracker();
-
- bool init(const std::vector<Predicate>& allConditionConfig,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
- std::vector<ConditionState>& conditionCache) override;
-
- bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
-
- void evaluateCondition(const LogEvent& event,
- const std::vector<MatchingState>& eventMatcherValues,
- const std::vector<sp<ConditionTracker>>& mAllConditions,
- std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache) override;
-
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- const bool isPartialLink,
- std::vector<ConditionState>& conditionCache) const override;
-
- virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- if (mSliced) {
- return &mLastChangedToTrueDimensions;
- } else {
- return nullptr;
- }
- }
-
- virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- if (mSliced) {
- return &mLastChangedToFalseDimensions;
- } else {
- return nullptr;
- }
- }
-
- void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const override {
- for (const auto& itr : mSlicedConditionState) {
- if (itr.second > 0) {
- dimensions->insert(itr.first);
- }
- }
- }
-
- bool IsChangedDimensionTrackable() const override { return true; }
-
- bool IsSimpleCondition() const override { return true; }
-
- bool equalOutputDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensions) const override {
- return equalDimensions(mOutputDimensions, dimensions);
- }
-
-private:
- const ConfigKey mConfigKey;
- // The index of the LogEventMatcher which defines the start.
- int mStartLogMatcherIndex;
-
- // The index of the LogEventMatcher which defines the end.
- int mStopLogMatcherIndex;
-
- // if the start end needs to be nested.
- bool mCountNesting;
-
- // The index of the LogEventMatcher which defines the stop all.
- int mStopAllLogMatcherIndex;
-
- ConditionState mInitialValue;
-
- std::vector<Matcher> mOutputDimensions;
-
- bool mContainANYPositionInInternalDimensions;
-
- std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
- std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
-
- std::map<HashableDimensionKey, int> mSlicedConditionState;
-
- void setMatcherIndices(const SimplePredicate& predicate,
- const std::unordered_map<int64_t, int>& logTrackerMap);
-
- void handleStopAll(std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache);
-
- void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
- ConditionState* conditionCache, bool* changedCache);
-
- bool hitGuardRail(const HashableDimensionKey& newKey);
-
- void dumpState();
-
- FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
- FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
- FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // SIMPLE_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
deleted file mode 100644
index 60b8c53e91e1..000000000000
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ /dev/null
@@ -1,93 +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.
- */
-
-#include "Log.h"
-
-#include "condition_util.h"
-
-#include "../matchers/matcher_util.h"
-#include "ConditionTracker.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-
-ConditionState evaluateCombinationCondition(const std::vector<int>& children,
- const LogicalOperation& operation,
- const std::vector<ConditionState>& conditionCache) {
- ConditionState newCondition;
-
- bool hasUnknown = false;
- bool hasFalse = false;
- bool hasTrue = false;
-
- for (auto childIndex : children) {
- ConditionState childState = conditionCache[childIndex];
- if (childState == ConditionState::kUnknown) {
- hasUnknown = true;
- break;
- }
- if (childState == ConditionState::kFalse) {
- hasFalse = true;
- }
- if (childState == ConditionState::kTrue) {
- hasTrue = true;
- }
- }
-
- // If any child condition is in unknown state, the condition is unknown too.
- if (hasUnknown) {
- return ConditionState::kUnknown;
- }
-
- switch (operation) {
- case LogicalOperation::AND: {
- newCondition = hasFalse ? ConditionState::kFalse : ConditionState::kTrue;
- break;
- }
- case LogicalOperation::OR: {
- newCondition = hasTrue ? ConditionState::kTrue : ConditionState::kFalse;
- break;
- }
- case LogicalOperation::NOT:
- newCondition = children.empty() ? ConditionState::kUnknown :
- ((conditionCache[children[0]] == ConditionState::kFalse) ?
- ConditionState::kTrue : ConditionState::kFalse);
- break;
- case LogicalOperation::NAND:
- newCondition = hasFalse ? ConditionState::kTrue : ConditionState::kFalse;
- break;
- case LogicalOperation::NOR:
- newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
- break;
- case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
- newCondition = ConditionState::kFalse;
- break;
- }
- return newCondition;
-}
-
-ConditionState operator|(ConditionState l, ConditionState r) {
- return l >= r ? l : r;
-}
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
deleted file mode 100644
index fed90ec3da37..000000000000
--- a/cmds/statsd/src/condition/condition_util.h
+++ /dev/null
@@ -1,43 +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.
- */
-
-#ifndef CONDITION_UTIL_H
-#define CONDITION_UTIL_H
-
-#include <vector>
-#include "../matchers/matcher_util.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-enum ConditionState {
- kNotEvaluated = -2,
- kUnknown = -1,
- kFalse = 0,
- kTrue = 1,
-};
-
-ConditionState operator|(ConditionState l, ConditionState r);
-
-ConditionState evaluateCombinationCondition(const std::vector<int>& children,
- const LogicalOperation& operation,
- const std::vector<ConditionState>& conditionCache);
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // CONDITION_UTIL_H
diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp
deleted file mode 100644
index 4a2bd2799df8..000000000000
--- a/cmds/statsd/src/config/ConfigKey.cpp
+++ /dev/null
@@ -1,54 +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.
- */
-
-#include "config/ConfigKey.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-ConfigKey::ConfigKey() {
-}
-
-ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) {
-}
-
-ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) {
-}
-
-ConfigKey::~ConfigKey() {
-}
-
-string ConfigKey::ToString() const {
- string s;
- s += "(" + std::to_string(mUid) + " " + std::to_string(mId) + ")";
- return s;
-}
-
-
-int64_t StrToInt64(const string& str) {
- char* endp;
- int64_t value;
- value = strtoll(str.c_str(), &endp, 0);
- if (endp == str.c_str() || *endp != '\0') {
- value = 0;
- }
- return value;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h
deleted file mode 100644
index 4cc9393fbd02..000000000000
--- a/cmds/statsd/src/config/ConfigKey.h
+++ /dev/null
@@ -1,89 +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.
- */
-
-#pragma once
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-#include <string>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::hash;
-using std::string;
-
-/**
- * Uniquely identifies a configuration.
- */
-class ConfigKey {
-public:
- ConfigKey();
- ConfigKey(const ConfigKey& that);
- ConfigKey(int uid, const int64_t& id);
- ~ConfigKey();
-
- inline int GetUid() const {
- return mUid;
- }
- inline const int64_t& GetId() const {
- return mId;
- }
-
- inline bool operator<(const ConfigKey& that) const {
- if (mUid < that.mUid) {
- return true;
- }
- if (mUid > that.mUid) {
- return false;
- }
- return mId < that.mId;
- };
-
- inline bool operator==(const ConfigKey& that) const {
- return mUid == that.mUid && mId == that.mId;
- };
-
- string ToString() const;
-
-private:
- int64_t mId;
- int mUid;
-};
-
-int64_t StrToInt64(const string& str);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-/**
- * A hash function for ConfigKey so it can be used for unordered_map/set.
- * Unfortunately this has to go in std namespace because C++ is fun!
- */
-namespace std {
-
-using android::os::statsd::ConfigKey;
-
-template <>
-struct hash<ConfigKey> {
- std::size_t operator()(const ConfigKey& key) const {
- return (7 * key.GetUid()) ^ ((hash<long long>()(key.GetId())));
- }
-};
-
-} // namespace std
diff --git a/cmds/statsd/src/config/ConfigListener.cpp b/cmds/statsd/src/config/ConfigListener.cpp
deleted file mode 100644
index 21a3f1673fd7..000000000000
--- a/cmds/statsd/src/config/ConfigListener.cpp
+++ /dev/null
@@ -1,31 +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.
- */
-
-#include "config/ConfigListener.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-ConfigListener::ConfigListener() {
-}
-
-ConfigListener::~ConfigListener() {
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h
deleted file mode 100644
index 3d301379f359..000000000000
--- a/cmds/statsd/src/config/ConfigListener.h
+++ /dev/null
@@ -1,52 +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.
- */
-
-#pragma once
-
-#include "config/ConfigKey.h"
-
-#include <utils/RefBase.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::RefBase;
-
-/**
- * Callback for different subsystems inside statsd to implement to find out
- * when a configuration has been added, updated or removed.
- */
-class ConfigListener : public virtual RefBase {
-public:
- ConfigListener();
- virtual ~ConfigListener();
-
- /**
- * A configuration was added or updated.
- */
- virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
- const StatsdConfig& config, bool modularUpdate = false) = 0;
-
- /**
- * A configuration was removed.
- */
- virtual void OnConfigRemoved(const ConfigKey& key) = 0;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
deleted file mode 100644
index 13020e06dc5d..000000000000
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ /dev/null
@@ -1,375 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "config/ConfigManager.h"
-#include "storage/StorageManager.h"
-
-#include "guardrail/StatsdStats.h"
-#include "stats_log_util.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
-
-#include <stdio.h>
-#include <vector>
-#include "android-base/stringprintf.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::pair;
-using std::string;
-using std::vector;
-
-#define STATS_SERVICE_DIR "/data/misc/stats-service"
-
-using android::base::StringPrintf;
-using std::unique_ptr;
-
-struct ConfigReceiverDeathCookie {
- ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey,
- const shared_ptr<IPendingIntentRef>& pir) :
- mConfigManager(configManager), mConfigKey(configKey), mPir(pir) {
- }
-
- wp<ConfigManager> mConfigManager;
- ConfigKey mConfigKey;
- shared_ptr<IPendingIntentRef> mPir;
-};
-
-void ConfigManager::configReceiverDied(void* cookie) {
- auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
- sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
- if (!thiz) {
- return;
- }
-
- ConfigKey& configKey = cookie_->mConfigKey;
- shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
-
- // Erase the mapping from the config key to the config receiver (pir) if the
- // mapping still exists.
- lock_guard<mutex> lock(thiz->mMutex);
- auto it = thiz->mConfigReceivers.find(configKey);
- if (it != thiz->mConfigReceivers.end() && it->second == pir) {
- thiz->mConfigReceivers.erase(configKey);
- }
-
- // The death recipient corresponding to this specific pir can never be
- // triggered again, so free up resources.
- delete cookie_;
-}
-
-struct ActiveConfigChangedReceiverDeathCookie {
- ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid,
- const shared_ptr<IPendingIntentRef>& pir) :
- mConfigManager(configManager), mUid(uid), mPir(pir) {
- }
-
- wp<ConfigManager> mConfigManager;
- int mUid;
- shared_ptr<IPendingIntentRef> mPir;
-};
-
-void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
- auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
- sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
- if (!thiz) {
- return;
- }
-
- int uid = cookie_->mUid;
- shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
-
- // Erase the mapping from the config key to the active config changed
- // receiver (pir) if the mapping still exists.
- lock_guard<mutex> lock(thiz->mMutex);
- auto it = thiz->mActiveConfigsChangedReceivers.find(uid);
- if (it != thiz->mActiveConfigsChangedReceivers.end() && it->second == pir) {
- thiz->mActiveConfigsChangedReceivers.erase(uid);
- }
-
- // The death recipient corresponding to this specific pir can never
- // be triggered again, so free up resources.
- delete cookie_;
-}
-
-ConfigManager::ConfigManager() :
- mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
- mActiveConfigChangedReceiverDeathRecipient(
- AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
-}
-
-ConfigManager::~ConfigManager() {
-}
-
-void ConfigManager::Startup() {
- map<ConfigKey, StatsdConfig> configsFromDisk;
- StorageManager::readConfigFromDisk(configsFromDisk);
- for (const auto& pair : configsFromDisk) {
- UpdateConfig(pair.first, pair.second);
- }
-}
-
-void ConfigManager::StartupForTest() {
- // No-op function to avoid reading configs from disks for tests.
-}
-
-void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
- lock_guard<mutex> lock(mMutex);
- mListeners.push_back(listener);
-}
-
-void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) {
- vector<sp<ConfigListener>> broadcastList;
- {
- lock_guard <mutex> lock(mMutex);
-
- const int numBytes = config.ByteSize();
- vector<uint8_t> buffer(numBytes);
- config.SerializeToArray(&buffer[0], numBytes);
-
- auto uidIt = mConfigs.find(key.GetUid());
- // GuardRail: Limit the number of configs per uid.
- if (uidIt != mConfigs.end()) {
- auto it = uidIt->second.find(key);
- if (it == uidIt->second.end() &&
- uidIt->second.size() >= StatsdStats::kMaxConfigCountPerUid) {
- ALOGE("ConfigManager: uid %d has exceeded the config count limit", key.GetUid());
- return;
- }
- }
-
- // Check if it's a duplicate config.
- if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end() &&
- StorageManager::hasIdenticalConfig(key, buffer)) {
- // This is a duplicate config.
- ALOGI("ConfigManager This is a duplicate config %s", key.ToString().c_str());
- // Update saved file on disk. We still update timestamp of file when
- // there exists a duplicate configuration to avoid garbage collection.
- update_saved_configs_locked(key, buffer, numBytes);
- return;
- }
-
- // Update saved file on disk.
- update_saved_configs_locked(key, buffer, numBytes);
-
- // Add to set.
- mConfigs[key.GetUid()].insert(key);
-
- for (const sp<ConfigListener>& listener : mListeners) {
- broadcastList.push_back(listener);
- }
- }
-
- const int64_t timestampNs = getElapsedRealtimeNs();
- // Tell everyone
- for (const sp<ConfigListener>& listener : broadcastList) {
- listener->OnConfigUpdated(timestampNs, key, config);
- }
-}
-
-void ConfigManager::SetConfigReceiver(const ConfigKey& key,
- const shared_ptr<IPendingIntentRef>& pir) {
- lock_guard<mutex> lock(mMutex);
- mConfigReceivers[key] = pir;
- AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(),
- new ConfigReceiverDeathCookie(this, key, pir));
-}
-
-void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
- lock_guard<mutex> lock(mMutex);
- mConfigReceivers.erase(key);
-}
-
-void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
- const shared_ptr<IPendingIntentRef>& pir) {
- {
- lock_guard<mutex> lock(mMutex);
- mActiveConfigsChangedReceivers[uid] = pir;
- }
- AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
- new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
-}
-
-void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
- lock_guard<mutex> lock(mMutex);
- mActiveConfigsChangedReceivers.erase(uid);
-}
-
-void ConfigManager::RemoveConfig(const ConfigKey& key) {
- vector<sp<ConfigListener>> broadcastList;
- {
- lock_guard <mutex> lock(mMutex);
-
- auto uid = key.GetUid();
- auto uidIt = mConfigs.find(uid);
- if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) {
- // Remove from map
- uidIt->second.erase(key);
-
- for (const sp<ConfigListener>& listener : mListeners) {
- broadcastList.push_back(listener);
- }
- }
-
- // Remove from disk. There can still be a lingering file on disk so we check
- // whether or not the config was on memory.
- remove_saved_configs(key);
- }
-
- for (const sp<ConfigListener>& listener:broadcastList) {
- listener->OnConfigRemoved(key);
- }
-}
-
-void ConfigManager::remove_saved_configs(const ConfigKey& key) {
- string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
- StorageManager::deleteSuffixedFiles(STATS_SERVICE_DIR, suffix.c_str());
-}
-
-void ConfigManager::RemoveConfigs(int uid) {
- vector<ConfigKey> removed;
- vector<sp<ConfigListener>> broadcastList;
- {
- lock_guard <mutex> lock(mMutex);
-
- auto uidIt = mConfigs.find(uid);
- if (uidIt == mConfigs.end()) {
- return;
- }
-
- for (auto it = uidIt->second.begin(); it != uidIt->second.end(); ++it) {
- // Remove from map
- remove_saved_configs(*it);
- removed.push_back(*it);
- }
-
- mConfigs.erase(uidIt);
-
- for (const sp<ConfigListener>& listener : mListeners) {
- broadcastList.push_back(listener);
- }
- }
-
- // Remove separately so if they do anything in the callback they can't mess up our iteration.
- for (auto& key : removed) {
- // Tell everyone
- for (const sp<ConfigListener>& listener:broadcastList) {
- listener->OnConfigRemoved(key);
- }
- }
-}
-
-void ConfigManager::RemoveAllConfigs() {
- vector<ConfigKey> removed;
- vector<sp<ConfigListener>> broadcastList;
- {
- lock_guard <mutex> lock(mMutex);
-
- for (auto uidIt = mConfigs.begin(); uidIt != mConfigs.end();) {
- for (auto it = uidIt->second.begin(); it != uidIt->second.end();) {
- // Remove from map
- removed.push_back(*it);
- it = uidIt->second.erase(it);
- }
- uidIt = mConfigs.erase(uidIt);
- }
-
- for (const sp<ConfigListener>& listener : mListeners) {
- broadcastList.push_back(listener);
- }
- }
-
- // Remove separately so if they do anything in the callback they can't mess up our iteration.
- for (auto& key : removed) {
- // Tell everyone
- for (const sp<ConfigListener>& listener:broadcastList) {
- listener->OnConfigRemoved(key);
- }
- }
-}
-
-vector<ConfigKey> ConfigManager::GetAllConfigKeys() const {
- lock_guard<mutex> lock(mMutex);
-
- vector<ConfigKey> ret;
- for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) {
- for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) {
- ret.push_back(*it);
- }
- }
- return ret;
-}
-
-const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
- lock_guard<mutex> lock(mMutex);
-
- auto it = mConfigReceivers.find(key);
- if (it == mConfigReceivers.end()) {
- return nullptr;
- } else {
- return it->second;
- }
-}
-
-const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid)
- const {
- lock_guard<mutex> lock(mMutex);
-
- auto it = mActiveConfigsChangedReceivers.find(uid);
- if (it == mActiveConfigsChangedReceivers.end()) {
- return nullptr;
- } else {
- return it->second;
- }
-}
-
-void ConfigManager::Dump(FILE* out) {
- lock_guard<mutex> lock(mMutex);
-
- fprintf(out, "CONFIGURATIONS\n");
- fprintf(out, " uid name\n");
- for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) {
- for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) {
- fprintf(out, " %6d %lld\n", it->GetUid(), (long long)it->GetId());
- auto receiverIt = mConfigReceivers.find(*it);
- if (receiverIt != mConfigReceivers.end()) {
- fprintf(out, " -> received by PendingIntent as binder\n");
- }
- }
- }
-}
-
-void ConfigManager::update_saved_configs_locked(const ConfigKey& key,
- const vector<uint8_t>& buffer,
- const int numBytes) {
- // If there is a pre-existing config with same key we should first delete it.
- remove_saved_configs(key);
-
- // Then we save the latest config.
- string file_name =
- StringPrintf("%s/%ld_%d_%lld", STATS_SERVICE_DIR, time(nullptr),
- key.GetUid(), (long long)key.GetId());
- StorageManager::writeFile(file_name.c_str(), &buffer[0], numBytes);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
deleted file mode 100644
index bef057f96409..000000000000
--- a/cmds/statsd/src/config/ConfigManager.h
+++ /dev/null
@@ -1,181 +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.
- */
-
-#pragma once
-
-#include "config/ConfigKey.h"
-#include "config/ConfigListener.h"
-
-#include <aidl/android/os/IPendingIntentRef.h>
-#include <mutex>
-#include <string>
-
-#include <stdio.h>
-
-using aidl::android::os::IPendingIntentRef;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Keeps track of which configurations have been set from various sources.
- */
-class ConfigManager : public virtual android::RefBase {
-public:
- ConfigManager();
- virtual ~ConfigManager();
-
- /**
- * Initialize ConfigListener by reading from disk and get updates.
- */
- void Startup();
-
- /*
- * No-op initializer for tests.
- */
- void StartupForTest();
-
- /**
- * Someone else wants to know about the configs.
- */
- void AddListener(const sp<ConfigListener>& listener);
-
- /**
- * A configuration was added or updated.
- *
- * Reports this to listeners.
- */
- void UpdateConfig(const ConfigKey& key, const StatsdConfig& data);
-
- /**
- * Sets the broadcast receiver for a configuration key.
- */
- void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir);
-
- /**
- * Returns the package name and class name representing the broadcast receiver for this config.
- */
- const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
-
- /**
- * Returns all config keys registered.
- */
- std::vector<ConfigKey> GetAllConfigKeys() const;
-
- /**
- * Erase any broadcast receiver associated with this config key.
- */
- void RemoveConfigReceiver(const ConfigKey& key);
-
- /**
- * Sets the broadcast receiver that is notified whenever the list of active configs
- * changes for this uid.
- */
- void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir);
-
- /**
- * Returns the broadcast receiver for active configs changed for this uid.
- */
-
- const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
-
- /**
- * Erase any active configs changed broadcast receiver associated with this uid.
- */
- void RemoveActiveConfigsChangedReceiver(const int uid);
-
- /**
- * A configuration was removed.
- *
- * Reports this to listeners.
- */
- void RemoveConfig(const ConfigKey& key);
-
- /**
- * Remove all of the configs for the given uid.
- */
- void RemoveConfigs(int uid);
-
- /**
- * Remove all of the configs from memory.
- */
- void RemoveAllConfigs();
-
- /**
- * Text dump of our state for debugging.
- */
- void Dump(FILE* out);
-
-private:
- mutable std::mutex mMutex;
-
- /**
- * Save the configs to disk.
- */
- void update_saved_configs_locked(const ConfigKey& key,
- const std::vector<uint8_t>& buffer,
- const int numBytes);
-
- /**
- * Remove saved configs from disk.
- */
- void remove_saved_configs(const ConfigKey& key);
-
- /**
- * Maps from uid to the config keys that have been set.
- */
- std::map<int, std::set<ConfigKey>> mConfigs;
-
- /**
- * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
- */
- std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers;
-
- /**
- * Each uid can be subscribed by up to one receiver to notify that the list of active configs
- * for this uid has changed. The receiver is specified as IPendingIntentRef.
- */
- std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
-
- /**
- * The ConfigListeners that will be told about changes.
- */
- std::vector<sp<ConfigListener>> mListeners;
-
- // Death recipients that are triggered when the host process holding an
- // IPendingIntentRef dies.
- ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
- ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
-
- /**
- * Death recipient callback that is called when a config receiver dies.
- * The cookie is a pointer to a ConfigReceiverDeathCookie.
- */
- static void configReceiverDied(void* cookie);
-
- /**
- * Death recipient callback that is called when an active config changed
- * receiver dies. The cookie is a pointer to an
- * ActiveConfigChangedReceiverDeathCookie.
- */
- static void activeConfigChangedReceiverDied(void* cookie);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
deleted file mode 100644
index 85b660efc956..000000000000
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "config/ConfigKey.h"
-#include "Log.h"
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-
-#include <android-base/unique_fd.h>
-#include <inttypes.h>
-#include <sys/wait.h>
-
-#include <string>
-
-namespace {
-const char kDropboxTag[] = "perfetto";
-}
-
-namespace android {
-namespace os {
-namespace statsd {
-
-bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
- int64_t subscription_id,
- int64_t alert_id,
- const ConfigKey& configKey) {
- VLOG("Starting trace collection through perfetto");
-
- if (!config.has_trace_config()) {
- ALOGE("The perfetto trace config is empty, aborting");
- return false;
- }
-
- char subscriptionId[25];
- char alertId[25];
- char configId[25];
- char configUid[25];
- snprintf(subscriptionId, sizeof(subscriptionId), "%" PRId64, subscription_id);
- snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id);
- snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId());
- snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid());
-
- android::base::unique_fd readPipe;
- android::base::unique_fd writePipe;
- if (!android::base::Pipe(&readPipe, &writePipe)) {
- ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno));
- return false;
- }
-
- pid_t pid = fork();
- if (pid < 0) {
- ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno));
- return false;
- }
-
- if (pid == 0) {
- // Child process.
-
- // No malloc calls or library calls after this point. Remember that even
- // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf().
-
- writePipe.reset(); // Close the write end (owned by the main process).
-
- // Replace stdin with |readPipe| so the main process can write into it.
- if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1);
- readPipe.reset();
-
- // Replace stdout/stderr with /dev/null and close any other file
- // descriptor. This is to avoid SELinux complaining about perfetto
- // trying to access files accidentally left open by statsd (i.e. files
- // that have been opened without the O_CLOEXEC flag).
- int devNullFd = open("/dev/null", O_RDWR | O_CLOEXEC);
- if (dup2(devNullFd, STDOUT_FILENO) < 0) _exit(2);
- if (dup2(devNullFd, STDERR_FILENO) < 0) _exit(3);
- close(devNullFd);
- for (int i = 0; i < 1024; i++) {
- if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) close(i);
- }
-
- execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
- kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid",
- configUid, "--subscription-id", subscriptionId, nullptr);
-
- // execl() doesn't return in case of success, if we get here something
- // failed.
- _exit(4);
- }
-
- // Main process.
-
- readPipe.reset(); // Close the read end (owned by the child process).
-
- // Using fdopen() because fwrite() has the right logic to chunking write()
- // over a pipe (see __sfvwrite()).
- FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb");
- if (!writePipeStream) {
- ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
- return false;
- }
-
- const std::string& cfgProto = config.trace_config();
- size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
- fclose(writePipeStream);
- if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
- ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten,
- strerror(errno));
- return false;
- }
-
- // This does NOT wait for the full duration of the trace. It just waits until
- // the process has read the config from stdin and detached.
- int childStatus = 0;
- waitpid(pid, &childStatus, 0);
- if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) {
- ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus);
- return false;
- }
-
- VLOG("CollectPerfettoTraceAndUploadToDropbox() succeeded");
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
deleted file mode 100644
index 095782a49f9b..000000000000
--- a/cmds/statsd/src/external/Perfetto.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class ConfigKey;
-class PerfettoDetails; // Declared in statsd_config.pb.h
-
-// Starts the collection of a Perfetto trace with the given |config|.
-// The trace is uploaded to Dropbox by the perfetto cmdline util once done.
-// This method returns immediately after passing the config and does NOT wait
-// for the full duration of the trace.
-bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
- int64_t subscription_id,
- int64_t alert_id,
- const ConfigKey& configKey);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/PullDataReceiver.h b/cmds/statsd/src/external/PullDataReceiver.h
deleted file mode 100644
index dd5c0cfa04c1..000000000000
--- a/cmds/statsd/src/external/PullDataReceiver.h
+++ /dev/null
@@ -1,41 +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.
- */
-#pragma once
-
-#include <utils/RefBase.h>
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class PullDataReceiver : virtual public RefBase{
- public:
- virtual ~PullDataReceiver() {}
- /**
- * @param data The pulled data.
- * @param pullSuccess Whether the pull succeeded. If the pull does not succeed, the data for the
- * bucket should be invalidated.
- * @param originalPullTimeNs This is when all the pulls have been initiated (elapsed time).
- */
- virtual void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
- bool pullSuccess, int64_t originalPullTimeNs) = 0;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp
deleted file mode 100644
index 8aa4792dc179..000000000000
--- a/cmds/statsd/src/external/PullResultReceiver.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 "PullResultReceiver.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-PullResultReceiver::PullResultReceiver(
- std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb)
- : pullFinishCallback(std::move(pullFinishCb)) {
-}
-
-Status PullResultReceiver::pullFinished(int32_t atomTag, bool success,
- const vector<StatsEventParcel>& output) {
- pullFinishCallback(atomTag, success, output);
- return Status::ok();
-}
-
-PullResultReceiver::~PullResultReceiver() {
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h
deleted file mode 100644
index ceaae801b2d5..000000000000
--- a/cmds/statsd/src/external/PullResultReceiver.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 <aidl/android/os/BnPullAtomResultReceiver.h>
-#include <aidl/android/util/StatsEventParcel.h>
-
-using namespace std;
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::os::BnPullAtomResultReceiver;
-using aidl::android::util::StatsEventParcel;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class PullResultReceiver : public BnPullAtomResultReceiver {
-public:
- PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
- pullFinishCallback);
- ~PullResultReceiver();
-
- /**
- * Binder call for finishing a pull.
- */
- Status pullFinished(int32_t atomTag, bool success,
- const vector<StatsEventParcel>& output) override;
-
-private:
- function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/PullUidProvider.h b/cmds/statsd/src/external/PullUidProvider.h
deleted file mode 100644
index 2318c501ea4b..000000000000
--- a/cmds/statsd/src/external/PullUidProvider.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-#pragma once
-
-#include <utils/RefBase.h>
-
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class PullUidProvider : virtual public RefBase {
-public:
- virtual ~PullUidProvider() {}
-
- /**
- * @param atomId The atom for which to get the uids.
- */
- virtual vector<int32_t> getPullAtomUids(int32_t atomId) = 0;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
deleted file mode 100644
index 78e6f094db7e..000000000000
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StatsCallbackPuller.h"
-#include "PullResultReceiver.h"
-#include "StatsPullerManager.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-
-#include <aidl/android/util/StatsEventParcel.h>
-
-using namespace std;
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::util::StatsEventParcel;
-using ::ndk::SharedRefBase;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
- const int64_t coolDownNs, int64_t timeoutNs,
- const vector<int> additiveFields)
- : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
- VLOG("StatsCallbackPuller created for tag %d", tagId);
-}
-
-bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- VLOG("StatsCallbackPuller called for tag %d", mTagId);
- if(mCallback == nullptr) {
- ALOGW("No callback registered");
- return false;
- }
-
- // Shared variables needed in the result receiver.
- shared_ptr<mutex> cv_mutex = make_shared<mutex>();
- shared_ptr<condition_variable> cv = make_shared<condition_variable>();
- shared_ptr<bool> pullFinish = make_shared<bool>(false);
- shared_ptr<bool> pullSuccess = make_shared<bool>(false);
- shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =
- make_shared<vector<shared_ptr<LogEvent>>>();
-
- shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>(
- [cv_mutex, cv, pullFinish, pullSuccess, sharedData](
- int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {
- // This is the result of the pull, executing in a statsd binder thread.
- // The pull could have taken a long time, and we should only modify
- // data (the output param) if the pointer is in scope and the pull did not time out.
- {
- lock_guard<mutex> lk(*cv_mutex);
- for (const StatsEventParcel& parcel: output) {
- shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
- bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(),
- parcel.buffer.size());
- if (valid) {
- sharedData->push_back(event);
- } else {
- StatsdStats::getInstance().noteAtomError(event->GetTagId(),
- /*pull=*/true);
- }
- }
- *pullSuccess = success;
- *pullFinish = true;
- }
- cv->notify_one();
- });
-
- // Initiate the pull. This is a oneway call to a different process, except
- // in unit tests. In process calls are not oneway.
- Status status = mCallback->onPullAtom(mTagId, resultReceiver);
- if (!status.isOk()) {
- StatsdStats::getInstance().notePullBinderCallFailed(mTagId);
- return false;
- }
-
- {
- unique_lock<mutex> unique_lk(*cv_mutex);
- // Wait until the pull finishes, or until the pull timeout.
- cv->wait_for(unique_lk, chrono::nanoseconds(mPullTimeoutNs),
- [pullFinish] { return *pullFinish; });
- if (!*pullFinish) {
- // Note: The parent stats puller will also note that there was a timeout and that the
- // cache should be cleared. Once we migrate all pullers to this callback, we could
- // consolidate the logic.
- return true;
- } else {
- // Only copy the data if we did not timeout and the pull was successful.
- if (*pullSuccess) {
- *data = std::move(*sharedData);
- }
- VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
- return *pullSuccess;
- }
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
deleted file mode 100644
index e82e8bb532be..000000000000
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <aidl/android/os/IPullAtomCallback.h>
-#include "StatsPuller.h"
-
-using aidl::android::os::IPullAtomCallback;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StatsCallbackPuller : public StatsPuller {
-public:
- explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
- const int64_t coolDownNs, const int64_t timeoutNs,
- const std::vector<int> additiveFields);
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
- const shared_ptr<IPullAtomCallback> mCallback;
-
- FRIEND_TEST(StatsCallbackPullerTest, PullFail);
- FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
- FRIEND_TEST(StatsCallbackPullerTest, PullTimeout);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
deleted file mode 100644
index bb5d0a6bab58..000000000000
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ /dev/null
@@ -1,126 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StatsPuller.h"
-#include "StatsPullerManager.h"
-#include "guardrail/StatsdStats.h"
-#include "puller_util.h"
-#include "stats_log_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::lock_guard;
-
-sp<UidMap> StatsPuller::mUidMap = nullptr;
-void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
-
-StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs,
- const std::vector<int> additiveFields)
- : mTagId(tagId),
- mPullTimeoutNs(pullTimeoutNs),
- mCoolDownNs(coolDownNs),
- mAdditiveFields(additiveFields),
- mLastPullTimeNs(0),
- mLastEventTimeNs(0) {
-}
-
-bool StatsPuller::Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
- lock_guard<std::mutex> lock(mLock);
- const int64_t elapsedTimeNs = getElapsedRealtimeNs();
- const int64_t systemUptimeMillis = getSystemUptimeMillis();
- StatsdStats::getInstance().notePull(mTagId);
- const bool shouldUseCache =
- (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs);
- if (shouldUseCache) {
- if (mHasGoodData) {
- (*data) = mCachedData;
- StatsdStats::getInstance().notePullFromCache(mTagId);
-
- }
- return mHasGoodData;
- }
- if (mLastPullTimeNs > 0) {
- StatsdStats::getInstance().updateMinPullIntervalSec(
- mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
- }
- mCachedData.clear();
- mLastPullTimeNs = elapsedTimeNs;
- mLastEventTimeNs = eventTimeNs;
- mHasGoodData = PullInternal(&mCachedData);
- if (!mHasGoodData) {
- return mHasGoodData;
- }
- const int64_t pullElapsedDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
- const int64_t pullSystemUptimeDurationMillis = getSystemUptimeMillis() - systemUptimeMillis;
- StatsdStats::getInstance().notePullTime(mTagId, pullElapsedDurationNs);
- const bool pullTimeOut = pullElapsedDurationNs > mPullTimeoutNs;
- if (pullTimeOut) {
- // Something went wrong. Discard the data.
- mCachedData.clear();
- mHasGoodData = false;
- StatsdStats::getInstance().notePullTimeout(
- mTagId, pullSystemUptimeDurationMillis, NanoToMillis(pullElapsedDurationNs));
- ALOGW("Pull for atom %d exceeds timeout %lld nano seconds.", mTagId,
- (long long)pullElapsedDurationNs);
- return mHasGoodData;
- }
-
- if (mCachedData.size() > 0) {
- mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields);
- }
-
- if (mCachedData.empty()) {
- VLOG("Data pulled is empty");
- StatsdStats::getInstance().noteEmptyData(mTagId);
- }
-
- (*data) = mCachedData;
- return mHasGoodData;
-}
-
-int StatsPuller::ForceClearCache() {
- return clearCache();
-}
-
-int StatsPuller::clearCache() {
- lock_guard<std::mutex> lock(mLock);
- return clearCacheLocked();
-}
-
-int StatsPuller::clearCacheLocked() {
- int ret = mCachedData.size();
- mCachedData.clear();
- mLastPullTimeNs = 0;
- mLastEventTimeNs = 0;
- return ret;
-}
-
-int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
- if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
- return clearCache();
- } else {
- return 0;
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
deleted file mode 100644
index 470d15e6fbd1..000000000000
--- a/cmds/statsd/src/external/StatsPuller.h
+++ /dev/null
@@ -1,119 +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.
- */
-
-#pragma once
-
-#include <aidl/android/os/IStatsCompanionService.h>
-#include <utils/RefBase.h>
-#include <mutex>
-#include <vector>
-#include "packages/UidMap.h"
-
-#include "guardrail/StatsdStats.h"
-#include "logd/LogEvent.h"
-#include "puller_util.h"
-
-using aidl::android::os::IStatsCompanionService;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StatsPuller : public virtual RefBase {
-public:
- explicit StatsPuller(const int tagId,
- const int64_t coolDownNs = NS_PER_SEC,
- const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs,
- const std::vector<int> additiveFields = std::vector<int>());
-
- virtual ~StatsPuller() {}
-
- // Pulls the most recent data.
- // The data may be served from cache if consecutive pulls come within
- // predefined cooldown time.
- // Returns true if the pull was successful.
- // Returns false when
- // 1) the pull fails
- // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
- // If a metric wants to make any change to the data, like timestamps, it
- // should make a copy as this data may be shared with multiple metrics.
- bool Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data);
-
- // Clear cache immediately
- int ForceClearCache();
-
- // Clear cache if elapsed time is more than cooldown time
- int ClearCacheIfNecessary(int64_t timestampNs);
-
- static void SetUidMap(const sp<UidMap>& uidMap);
-
- virtual void SetStatsCompanionService(
- shared_ptr<IStatsCompanionService> statsCompanionService) {};
-
-protected:
- const int mTagId;
-
- // Max time allowed to pull this atom.
- // We cannot reliably kill a pull thread. So we don't terminate the puller.
- // The data is discarded if the pull takes longer than this and mHasGoodData
- // marked as false.
- const int64_t mPullTimeoutNs = StatsdStats::kPullMaxDelayNs;
-
-private:
- mutable std::mutex mLock;
-
- // Real puller impl.
- virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
-
- bool mHasGoodData = false;
-
- // Minimum time before this puller does actual pull again.
- // Pullers can cause significant impact to system health and battery.
- // So that we don't pull too frequently.
- // If a pull request comes before cooldown, a cached version from previous pull
- // will be returned.
- const int64_t mCoolDownNs = 1 * NS_PER_SEC;
-
- // The field numbers of the fields that need to be summed when merging
- // isolated uid with host uid.
- const std::vector<int> mAdditiveFields;
-
- int64_t mLastPullTimeNs;
-
- // All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc).
- // If multiple pulls need to be done at the same event time, we will always use the cache after
- // the first pull.
- int64_t mLastEventTimeNs;
-
- // Cache of data from last pull. If next request comes before cool down finishes,
- // cached data will be returned.
- // Cached data is cleared when
- // 1) A pull fails
- // 2) A new pull request comes after cooldown time.
- // 3) clearCache is called.
- std::vector<std::shared_ptr<LogEvent>> mCachedData;
-
- int clearCache();
-
- int clearCacheLocked();
-
- static sp<UidMap> mUidMap;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
deleted file mode 100644
index 8334b6b4db90..000000000000
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ /dev/null
@@ -1,371 +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.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include "StatsPullerManager.h"
-
-#include <cutils/log.h>
-#include <math.h>
-#include <stdint.h>
-
-#include <algorithm>
-#include <iostream>
-
-#include "../StatsService.h"
-#include "../logd/LogEvent.h"
-#include "../stats_log_util.h"
-#include "../statscompanion_util.h"
-#include "StatsCallbackPuller.h"
-#include "TrainInfoPuller.h"
-#include "statslog_statsd.h"
-
-using std::shared_ptr;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Stores the puller as a wp to avoid holding a reference in case it is unregistered and
-// pullAtomCallbackDied is never called.
-struct PullAtomCallbackDeathCookie {
- PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager,
- const PullerKey& pullerKey, const wp<StatsPuller>& puller) :
- mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
- }
-
- wp<StatsPullerManager> mPullerManager;
- PullerKey mPullerKey;
- wp<StatsPuller> mPuller;
-};
-
-void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
- PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
- sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote();
- if (!thiz) {
- return;
- }
-
- const PullerKey& pullerKey = cookie_->mPullerKey;
- wp<StatsPuller> puller = cookie_->mPuller;
-
- // Erase the mapping from the puller key to the puller if the mapping still exists.
- // Note that we are removing the StatsPuller object, which internally holds the binder
- // IPullAtomCallback. However, each new registration creates a new StatsPuller, so this works.
- lock_guard<mutex> lock(thiz->mLock);
- const auto& it = thiz->kAllPullAtomInfo.find(pullerKey);
- if (it != thiz->kAllPullAtomInfo.end() && puller != nullptr && puller == it->second) {
- StatsdStats::getInstance().notePullerCallbackRegistrationChanged(pullerKey.atomTag,
- /*registered=*/false);
- thiz->kAllPullAtomInfo.erase(pullerKey);
- }
- // The death recipient corresponding to this specific IPullAtomCallback can never
- // be triggered again, so free up resources.
- delete cookie_;
-}
-
-// Values smaller than this may require to update the alarm.
-const int64_t NO_ALARM_UPDATE = INT64_MAX;
-
-StatsPullerManager::StatsPullerManager()
- : kAllPullAtomInfo({
- // TrainInfo.
- {{.atomTag = util::TRAIN_INFO, .uid = AID_STATSD}, new TrainInfoPuller()},
- }),
- mNextPullTimeNs(NO_ALARM_UPDATE),
- mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) {
-}
-
-bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, configKey, eventTimeNs, data);
-}
-
-bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> _l(mLock);
- return PullLocked(tagId, uids, eventTimeNs, data);
-}
-
-bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
- vector<int32_t> uids;
- const auto& uidProviderIt = mPullUidProviders.find(configKey);
- if (uidProviderIt == mPullUidProviders.end()) {
- ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
- if (pullUidProvider == nullptr) {
- ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
- configKey.ToString().c_str());
- StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
- return false;
- }
- uids = pullUidProvider->getPullAtomUids(tagId);
- return PullLocked(tagId, uids, eventTimeNs, data);
-}
-
-bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
- const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
- VLOG("Initiating pulling %d", tagId);
- for (int32_t uid : uids) {
- PullerKey key = {.atomTag = tagId, .uid = uid};
- auto pullerIt = kAllPullAtomInfo.find(key);
- if (pullerIt != kAllPullAtomInfo.end()) {
- bool ret = pullerIt->second->Pull(eventTimeNs, data);
- VLOG("pulled %zu items", data->size());
- if (!ret) {
- StatsdStats::getInstance().notePullFailed(tagId);
- }
- return ret;
- }
- }
- StatsdStats::getInstance().notePullerNotFound(tagId);
- ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
- return false; // Return early since we don't know what to pull.
-}
-
-bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
- // Pulled atoms might be registered after we parse the config, so just make sure the id is in
- // an appropriate range.
- return isVendorPulledAtom(tagId) || isPulledAtom(tagId);
-}
-
-void StatsPullerManager::updateAlarmLocked() {
- if (mNextPullTimeNs == NO_ALARM_UPDATE) {
- VLOG("No need to set alarms. Skipping");
- return;
- }
-
- // TODO(b/151045771): do not hold a lock while making a binder call
- if (mStatsCompanionService != nullptr) {
- mStatsCompanionService->setPullingAlarm(mNextPullTimeNs / 1000000);
- } else {
- VLOG("StatsCompanionService not available. Alarm not set.");
- }
- return;
-}
-
-void StatsPullerManager::SetStatsCompanionService(
- shared_ptr<IStatsCompanionService> statsCompanionService) {
- std::lock_guard<std::mutex> _l(mLock);
- shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
- mStatsCompanionService = statsCompanionService;
- for (const auto& pulledAtom : kAllPullAtomInfo) {
- pulledAtom.second->SetStatsCompanionService(statsCompanionService);
- }
- if (mStatsCompanionService != nullptr) {
- updateAlarmLocked();
- }
-}
-
-void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
- int64_t intervalNs) {
- std::lock_guard<std::mutex> _l(mLock);
- auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}];
- for (auto it = receivers.begin(); it != receivers.end(); it++) {
- if (it->receiver == receiver) {
- VLOG("Receiver already registered of %d", (int)receivers.size());
- return;
- }
- }
- ReceiverInfo receiverInfo;
- receiverInfo.receiver = receiver;
-
- // Round it to the nearest minutes. This is the limit of alarm manager.
- // In practice, we should always have larger buckets.
- int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60;
- // Scheduled pulling should be at least 1 min apart.
- // This can be lower in cts tests, in which case we round it to 1 min.
- if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) {
- roundedIntervalNs = 60 * (int64_t)NS_PER_SEC;
- }
-
- receiverInfo.intervalNs = roundedIntervalNs;
- receiverInfo.nextPullTimeNs = nextPullTimeNs;
- receivers.push_back(receiverInfo);
-
- // There is only one alarm for all pulled events. So only set it to the smallest denom.
- if (nextPullTimeNs < mNextPullTimeNs) {
- VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs);
- mNextPullTimeNs = nextPullTimeNs;
- updateAlarmLocked();
- }
- VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
-}
-
-void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver) {
- std::lock_guard<std::mutex> _l(mLock);
- auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey});
- if (receiversIt == mReceivers.end()) {
- VLOG("Unknown pull code or no receivers: %d", tagId);
- return;
- }
- std::list<ReceiverInfo>& receivers = receiversIt->second;
- for (auto it = receivers.begin(); it != receivers.end(); it++) {
- if (receiver == it->receiver) {
- receivers.erase(it);
- VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size());
- return;
- }
- }
-}
-
-void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey,
- wp<PullUidProvider> provider) {
- std::lock_guard<std::mutex> _l(mLock);
- mPullUidProviders[configKey] = provider;
-}
-
-void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey,
- wp<PullUidProvider> provider) {
- std::lock_guard<std::mutex> _l(mLock);
- const auto& it = mPullUidProviders.find(configKey);
- if (it != mPullUidProviders.end() && it->second == provider) {
- mPullUidProviders.erase(it);
- }
-}
-
-void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
- std::lock_guard<std::mutex> _l(mLock);
- int64_t wallClockNs = getWallClockNs();
-
- int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
-
- vector<pair<const ReceiverKey*, vector<ReceiverInfo*>>> needToPull;
- for (auto& pair : mReceivers) {
- vector<ReceiverInfo*> receivers;
- if (pair.second.size() != 0) {
- for (ReceiverInfo& receiverInfo : pair.second) {
- if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
- receivers.push_back(&receiverInfo);
- } else {
- if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
- minNextPullTimeNs = receiverInfo.nextPullTimeNs;
- }
- }
- }
- if (receivers.size() > 0) {
- needToPull.push_back(make_pair(&pair.first, receivers));
- }
- }
- }
- for (const auto& pullInfo : needToPull) {
- vector<shared_ptr<LogEvent>> data;
- bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey,
- elapsedTimeNs, &data);
- if (!pullSuccess) {
- VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
- }
-
- // Convention is to mark pull atom timestamp at request time.
- // If we pull at t0, puller starts at t1, finishes at t2, and send back
- // at t3, we mark t0 as its timestamp, which should correspond to its
- // triggering event, such as condition change at t0.
- // Here the triggering event is alarm fired from AlarmManager.
- // In ValueMetricProducer and GaugeMetricProducer we do same thing
- // when pull on condition change, etc.
- for (auto& event : data) {
- event->setElapsedTimestampNs(elapsedTimeNs);
- event->setLogdWallClockTimestampNs(wallClockNs);
- }
-
- for (const auto& receiverInfo : pullInfo.second) {
- sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
- if (receiverPtr != nullptr) {
- receiverPtr->onDataPulled(data, pullSuccess, elapsedTimeNs);
- // We may have just come out of a coma, compute next pull time.
- int numBucketsAhead =
- (elapsedTimeNs - receiverInfo->nextPullTimeNs) / receiverInfo->intervalNs;
- receiverInfo->nextPullTimeNs += (numBucketsAhead + 1) * receiverInfo->intervalNs;
- if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
- minNextPullTimeNs = receiverInfo->nextPullTimeNs;
- }
- } else {
- VLOG("receiver already gone.");
- }
- }
- }
-
- VLOG("mNextPullTimeNs: %lld updated to %lld", (long long)mNextPullTimeNs,
- (long long)minNextPullTimeNs);
- mNextPullTimeNs = minNextPullTimeNs;
- updateAlarmLocked();
-}
-
-int StatsPullerManager::ForceClearPullerCache() {
- std::lock_guard<std::mutex> _l(mLock);
- int totalCleared = 0;
- for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second->ForceClearCache();
- }
- return totalCleared;
-}
-
-int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
- std::lock_guard<std::mutex> _l(mLock);
- int totalCleared = 0;
- for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
- }
- return totalCleared;
-}
-
-void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
- const int64_t coolDownNs, const int64_t timeoutNs,
- const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback) {
- std::lock_guard<std::mutex> _l(mLock);
- VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
-
- if (callback == nullptr) {
- ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag);
- return;
- }
-
- StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
- int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs;
- int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs;
-
- sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
- actualTimeoutNs, additiveFields);
- PullerKey key = {.atomTag = atomTag, .uid = uid};
- AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
- new PullAtomCallbackDeathCookie(this, key, puller));
- kAllPullAtomInfo[key] = puller;
-}
-
-void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
- std::lock_guard<std::mutex> _l(mLock);
- PullerKey key = {.atomTag = atomTag, .uid = uid};
- if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
- StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
- /*registered=*/false);
- kAllPullAtomInfo.erase(key);
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
deleted file mode 100644
index 489cbdbe5400..000000000000
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ /dev/null
@@ -1,189 +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.
- */
-
-#pragma once
-
-#include <aidl/android/os/IPullAtomCallback.h>
-#include <aidl/android/os/IStatsCompanionService.h>
-#include <utils/RefBase.h>
-
-#include <list>
-#include <vector>
-
-#include "PullDataReceiver.h"
-#include "PullUidProvider.h"
-#include "StatsPuller.h"
-#include "guardrail/StatsdStats.h"
-#include "logd/LogEvent.h"
-#include "packages/UidMap.h"
-
-using aidl::android::os::IPullAtomCallback;
-using aidl::android::os::IStatsCompanionService;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-typedef struct PullerKey {
- // The uid of the process that registers this puller.
- const int uid = -1;
- // The atom that this puller is for.
- const int atomTag;
-
- bool operator<(const PullerKey& that) const {
- if (uid < that.uid) {
- return true;
- }
- if (uid > that.uid) {
- return false;
- }
- return atomTag < that.atomTag;
- };
-
- bool operator==(const PullerKey& that) const {
- return uid == that.uid && atomTag == that.atomTag;
- };
-} PullerKey;
-
-class StatsPullerManager : public virtual RefBase {
-public:
- StatsPullerManager();
-
- virtual ~StatsPullerManager() {
- }
-
-
- // Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
- // and then every intervalNs thereafter.
- virtual void RegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
- int64_t intervalNs);
-
- // Stop listening on a tagId.
- virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey,
- wp<PullDataReceiver> receiver);
-
- // Registers a pull uid provider for the config key. When pulling atoms, it will be used to
- // determine which uids to pull from.
- virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider);
-
- // Unregister a pull uid provider.
- virtual void UnregisterPullUidProvider(const ConfigKey& configKey,
- wp<PullUidProvider> provider);
-
- // Verify if we know how to pull for this matcher
- bool PullerForMatcherExists(int tagId) const;
-
- void OnAlarmFired(int64_t elapsedTimeNs);
-
- // Pulls the most recent data.
- // The data may be served from cache if consecutive pulls come within
- // mCoolDownNs.
- // Returns true if the pull was successful.
- // Returns false when
- // 1) the pull fails
- // 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
- // 3) Either a PullUidProvider was not registered for the config, or the there was no puller
- // registered for any of the uids for this atom.
- // If the metric wants to make any change to the data, like timestamps, they
- // should make a copy as this data may be shared with multiple metrics.
- virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data);
-
- // Same as above, but directly specify the allowed uids to pull from.
- virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data);
-
- // Clear pull data cache immediately.
- int ForceClearPullerCache();
-
- // Clear pull data cache if it is beyond respective cool down time.
- int ClearPullerCacheIfNecessary(int64_t timestampNs);
-
- void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
-
- void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
- const int64_t timeoutNs, const vector<int32_t>& additiveFields,
- const shared_ptr<IPullAtomCallback>& callback);
-
- void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
-
- std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
-
-private:
- const static int64_t kMinCoolDownNs = NS_PER_SEC;
- const static int64_t kMaxTimeoutNs = 10 * NS_PER_SEC;
- shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
-
- // A struct containing an atom id and a Config Key
- typedef struct ReceiverKey {
- const int atomTag;
- const ConfigKey configKey;
-
- inline bool operator<(const ReceiverKey& that) const {
- return atomTag == that.atomTag ? configKey < that.configKey : atomTag < that.atomTag;
- }
- } ReceiverKey;
-
- typedef struct {
- int64_t nextPullTimeNs;
- int64_t intervalNs;
- wp<PullDataReceiver> receiver;
- } ReceiverInfo;
-
- // mapping from Receiver Key to receivers
- std::map<ReceiverKey, std::list<ReceiverInfo>> mReceivers;
-
- // mapping from Config Key to the PullUidProvider for that config
- std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
-
- bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data);
-
- bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data);
-
- // locks for data receiver and StatsCompanionService changes
- std::mutex mLock;
-
- void updateAlarmLocked();
-
- int64_t mNextPullTimeNs;
-
- // Death recipient that is triggered when the process holding the IPullAtomCallback has died.
- ::ndk::ScopedAIBinder_DeathRecipient mPullAtomCallbackDeathRecipient;
-
- /**
- * Death recipient callback that is called when a pull atom callback dies.
- * The cookie is a pointer to a PullAtomCallbackDeathCookie.
- */
- static void pullAtomCallbackDied(void* cookie);
-
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
-
- FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
deleted file mode 100644
index 3837f4a1a517..000000000000
--- a/cmds/statsd/src/external/TrainInfoPuller.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "external/StatsPuller.h"
-
-#include "TrainInfoPuller.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-#include "statslog_statsd.h"
-#include "storage/StorageManager.h"
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-TrainInfoPuller::TrainInfoPuller() :
- StatsPuller(util::TRAIN_INFO) {
-}
-
-bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- vector<InstallTrainInfo> trainInfoList =
- StorageManager::readAllTrainInfo();
- if (trainInfoList.empty()) {
- ALOGW("Train info was empty.");
- return true;
- }
- for (InstallTrainInfo& trainInfo : trainInfoList) {
- auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
- data->push_back(event);
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/TrainInfoPuller.h b/cmds/statsd/src/external/TrainInfoPuller.h
deleted file mode 100644
index 615d02351fd3..000000000000
--- a/cmds/statsd/src/external/TrainInfoPuller.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Reads train info from disk.
- */
-class TrainInfoPuller : public StatsPuller {
- public:
- TrainInfoPuller();
-
- private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
deleted file mode 100644
index aa99d0082bdd..000000000000
--- a/cmds/statsd/src/external/puller_util.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "puller_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using namespace std;
-
-/**
- * Process all data and merge isolated with host if necessary.
- * For example:
- * NetworkBytesAtom {
- * int uid = 1;
- * State process_state = 2;
- * int byte_send = 3;
- * int byte_recv = 4;
- * }
- * additive fields are {3, 4}
- * If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
- * [uid1, fg, 100, 200]
- * [uid1_child, fg, 100, 200]
- * [uid1, bg, 100, 200]
- *
- * We want to merge them and results should be:
- * [uid1, fg, 200, 400]
- * [uid1, bg, 100, 200]
- *
- * All atoms should be of the same tagId. All fields should be present.
- */
-void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
- int tagId, const vector<int>& additiveFieldsVec) {
- // Check the first LogEvent for attribution chain or a uid field as either all atoms with this
- // tagId have them or none of them do.
- std::pair<int, int> attrIndexRange;
- const bool hasAttributionChain = data[0]->hasAttributionChain(&attrIndexRange);
- bool hasUidField = (data[0]->getUidFieldIndex() != -1);
-
- if (!hasAttributionChain && !hasUidField) {
- VLOG("No uid or attribution chain to merge, atom %d", tagId);
- return;
- }
-
- // 1. Map all isolated uid in-place to host uid
- for (shared_ptr<LogEvent>& event : data) {
- if (event->GetTagId() != tagId) {
- ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
- return;
- }
- if (hasAttributionChain) {
- vector<FieldValue>* const fieldValues = event->getMutableValues();
- for (int i = attrIndexRange.first; i <= attrIndexRange.second; i++) {
- FieldValue& fieldValue = fieldValues->at(i);
- if (isAttributionUidField(fieldValue)) {
- const int hostUid = uidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
- fieldValue.mValue.setInt(hostUid);
- }
- }
- } else {
- int uidFieldIndex = event->getUidFieldIndex();
- if (uidFieldIndex != -1) {
- Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
- const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
- value.setInt(hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- }
- }
- }
-
- // 2. sort the data, bit-wise
- sort(data.begin(), data.end(),
- [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) {
- if (lhs->size() != rhs->size()) {
- return lhs->size() < rhs->size();
- }
- const std::vector<FieldValue>& lhsValues = lhs->getValues();
- const std::vector<FieldValue>& rhsValues = rhs->getValues();
- for (int i = 0; i < (int)lhs->size(); i++) {
- if (lhsValues[i] != rhsValues[i]) {
- return lhsValues[i] < rhsValues[i];
- }
- }
- return false;
- });
-
- vector<shared_ptr<LogEvent>> mergedData;
- const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
- bool needMerge = true;
-
- // 3. do the merge.
- // The loop invariant is this: for every event, check if it differs on
- // non-additive fields, or have different attribution chain length.
- // If so, no need to merge, add itself to the result.
- // Otherwise, merge the value onto the one immediately next to it.
- for (int i = 0; i < (int)data.size() - 1; i++) {
- // Size different, must be different chains.
- if (data[i]->size() != data[i + 1]->size()) {
- mergedData.push_back(data[i]);
- continue;
- }
- vector<FieldValue>* lhsValues = data[i]->getMutableValues();
- vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues();
- needMerge = true;
- for (int p = 0; p < (int)lhsValues->size(); p++) {
- if ((*lhsValues)[p] != (*rhsValues)[p]) {
- int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
- // Differ on non-additive field, abort.
- if (additiveFields.find(pos) == additiveFields.end()) {
- needMerge = false;
- break;
- }
- }
- }
- if (!needMerge) {
- mergedData.push_back(data[i]);
- continue;
- }
- // This should be infrequent operation.
- for (int p = 0; p < (int)lhsValues->size(); p++) {
- int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
- if (additiveFields.find(pos) != additiveFields.end()) {
- (*rhsValues)[p].mValue += (*lhsValues)[p].mValue;
- }
- }
- }
- mergedData.push_back(data.back());
-
- data.clear();
- data = mergedData;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
deleted file mode 100644
index afcf68ca10ad..000000000000
--- a/cmds/statsd/src/external/puller_util.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <vector>
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
-#include "packages/UidMap.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
- const sp<UidMap>& uidMap, int tagId,
- const vector<int>& additiveFieldsVec);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
deleted file mode 100644
index 6e89038f4152..000000000000
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ /dev/null
@@ -1,1101 +0,0 @@
-/*
- * Copyright 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.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StatsdStats.h"
-
-#include <android/util/ProtoOutputStream.h>
-#include "../stats_log_util.h"
-#include "statslog_statsd.h"
-#include "storage/StorageManager.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_FLOAT;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-using std::lock_guard;
-using std::shared_ptr;
-using std::string;
-using std::to_string;
-using std::vector;
-
-const int FIELD_ID_BEGIN_TIME = 1;
-const int FIELD_ID_END_TIME = 2;
-const int FIELD_ID_CONFIG_STATS = 3;
-const int FIELD_ID_ATOM_STATS = 7;
-const int FIELD_ID_UIDMAP_STATS = 8;
-const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
-const int FIELD_ID_PERIODIC_ALARM_STATS = 12;
-const int FIELD_ID_SYSTEM_SERVER_RESTART = 15;
-const int FIELD_ID_LOGGER_ERROR_STATS = 16;
-const int FIELD_ID_OVERFLOW = 18;
-const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19;
-
-const int FIELD_ID_ATOM_STATS_TAG = 1;
-const int FIELD_ID_ATOM_STATS_COUNT = 2;
-const int FIELD_ID_ATOM_STATS_ERROR_COUNT = 3;
-
-const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
-const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1;
-
-const int FIELD_ID_LOG_LOSS_STATS_TIME = 1;
-const int FIELD_ID_LOG_LOSS_STATS_COUNT = 2;
-const int FIELD_ID_LOG_LOSS_STATS_ERROR = 3;
-const int FIELD_ID_LOG_LOSS_STATS_TAG = 4;
-const int FIELD_ID_LOG_LOSS_STATS_UID = 5;
-const int FIELD_ID_LOG_LOSS_STATS_PID = 6;
-
-const int FIELD_ID_OVERFLOW_COUNT = 1;
-const int FIELD_ID_OVERFLOW_MAX_HISTORY = 2;
-const int FIELD_ID_OVERFLOW_MIN_HISTORY = 3;
-
-const int FIELD_ID_CONFIG_STATS_UID = 1;
-const int FIELD_ID_CONFIG_STATS_ID = 2;
-const int FIELD_ID_CONFIG_STATS_CREATION = 3;
-const int FIELD_ID_CONFIG_STATS_RESET = 19;
-const int FIELD_ID_CONFIG_STATS_DELETION = 4;
-const int FIELD_ID_CONFIG_STATS_METRIC_COUNT = 5;
-const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6;
-const int FIELD_ID_CONFIG_STATS_MATCHER_COUNT = 7;
-const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8;
-const int FIELD_ID_CONFIG_STATS_VALID = 9;
-const int FIELD_ID_CONFIG_STATS_BROADCAST = 10;
-const int FIELD_ID_CONFIG_STATS_DATA_DROP_TIME = 11;
-const int FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES = 21;
-const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME = 12;
-const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES = 20;
-const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13;
-const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14;
-const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15;
-const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16;
-const int FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS = 17;
-const int FIELD_ID_CONFIG_STATS_ANNOTATION = 18;
-const int FIELD_ID_CONFIG_STATS_ACTIVATION = 22;
-const int FIELD_ID_CONFIG_STATS_DEACTIVATION = 23;
-const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT64 = 1;
-const int FIELD_ID_CONFIG_STATS_ANNOTATION_INT32 = 2;
-
-const int FIELD_ID_MATCHER_STATS_ID = 1;
-const int FIELD_ID_MATCHER_STATS_COUNT = 2;
-const int FIELD_ID_CONDITION_STATS_ID = 1;
-const int FIELD_ID_CONDITION_STATS_COUNT = 2;
-const int FIELD_ID_METRIC_STATS_ID = 1;
-const int FIELD_ID_METRIC_STATS_COUNT = 2;
-const int FIELD_ID_ALERT_STATS_ID = 1;
-const int FIELD_ID_ALERT_STATS_COUNT = 2;
-
-const int FIELD_ID_UID_MAP_CHANGES = 1;
-const int FIELD_ID_UID_MAP_BYTES_USED = 2;
-const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 3;
-const int FIELD_ID_UID_MAP_DELETED_APPS = 4;
-
-const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID = 1;
-const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2;
-
-const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
- {util::BINDER_CALLS, {6000, 10000}},
- {util::LOOPER_STATS, {1500, 2500}},
- {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
-};
-
-StatsdStats::StatsdStats() {
- mPushedAtomStats.resize(kMaxPushedAtomId + 1);
- mStartTimeSec = getWallClockSec();
-}
-
-StatsdStats& StatsdStats::getInstance() {
- static StatsdStats statsInstance;
- return statsInstance;
-}
-
-void StatsdStats::addToIceBoxLocked(shared_ptr<ConfigStats>& stats) {
- // The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize.
- if (mIceBox.size() == kMaxIceBoxSize) {
- mIceBox.pop_front();
- }
- mIceBox.push_back(stats);
-}
-
-void StatsdStats::noteConfigReceived(
- const ConfigKey& key, int metricsCount, int conditionsCount, int matchersCount,
- int alertsCount, const std::list<std::pair<const int64_t, const int32_t>>& annotations,
- bool isValid) {
- lock_guard<std::mutex> lock(mLock);
- int32_t nowTimeSec = getWallClockSec();
-
- // If there is an existing config for the same key, icebox the old config.
- noteConfigRemovedInternalLocked(key);
-
- shared_ptr<ConfigStats> configStats = std::make_shared<ConfigStats>();
- configStats->uid = key.GetUid();
- configStats->id = key.GetId();
- configStats->creation_time_sec = nowTimeSec;
- configStats->metric_count = metricsCount;
- configStats->condition_count = conditionsCount;
- configStats->matcher_count = matchersCount;
- configStats->alert_count = alertsCount;
- configStats->is_valid = isValid;
- for (auto& v : annotations) {
- configStats->annotations.emplace_back(v);
- }
-
- if (isValid) {
- mConfigStats[key] = configStats;
- } else {
- configStats->deletion_time_sec = nowTimeSec;
- addToIceBoxLocked(configStats);
- }
-}
-
-void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) {
- auto it = mConfigStats.find(key);
- if (it != mConfigStats.end()) {
- int32_t nowTimeSec = getWallClockSec();
- it->second->deletion_time_sec = nowTimeSec;
- addToIceBoxLocked(it->second);
- mConfigStats.erase(it);
- }
-}
-
-void StatsdStats::noteConfigRemoved(const ConfigKey& key) {
- lock_guard<std::mutex> lock(mLock);
- noteConfigRemovedInternalLocked(key);
-}
-
-void StatsdStats::noteConfigResetInternalLocked(const ConfigKey& key) {
- auto it = mConfigStats.find(key);
- if (it != mConfigStats.end()) {
- it->second->reset_time_sec = getWallClockSec();
- }
-}
-
-void StatsdStats::noteConfigReset(const ConfigKey& key) {
- lock_guard<std::mutex> lock(mLock);
- noteConfigResetInternalLocked(key);
-}
-
-void StatsdStats::noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError,
- int32_t lastTag, int32_t uid, int32_t pid) {
- lock_guard<std::mutex> lock(mLock);
- if (mLogLossStats.size() == kMaxLoggerErrors) {
- mLogLossStats.pop_front();
- }
- mLogLossStats.emplace_back(wallClockTimeSec, count, lastError, lastTag, uid, pid);
-}
-
-void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
- noteBroadcastSent(key, getWallClockSec());
-}
-
-void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) {
- lock_guard<std::mutex> lock(mLock);
- auto it = mConfigStats.find(key);
- if (it == mConfigStats.end()) {
- ALOGE("Config key %s not found!", key.ToString().c_str());
- return;
- }
- if (it->second->broadcast_sent_time_sec.size() == kMaxTimestampCount) {
- it->second->broadcast_sent_time_sec.pop_front();
- }
- it->second->broadcast_sent_time_sec.push_back(timeSec);
-}
-
-void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated) {
- noteActiveStatusChanged(key, activated, getWallClockSec());
-}
-
-void StatsdStats::noteActiveStatusChanged(const ConfigKey& key, bool activated, int32_t timeSec) {
- lock_guard<std::mutex> lock(mLock);
- auto it = mConfigStats.find(key);
- if (it == mConfigStats.end()) {
- ALOGE("Config key %s not found!", key.ToString().c_str());
- return;
- }
- auto& vec = activated ? it->second->activation_time_sec
- : it->second->deactivation_time_sec;
- if (vec.size() == kMaxTimestampCount) {
- vec.pop_front();
- }
- vec.push_back(timeSec);
-}
-
-void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid) {
- noteActivationBroadcastGuardrailHit(uid, getWallClockSec());
-}
-
-void StatsdStats::noteActivationBroadcastGuardrailHit(const int uid, const int32_t timeSec) {
- lock_guard<std::mutex> lock(mLock);
- auto& guardrailTimes = mActivationBroadcastGuardrailStats[uid];
- if (guardrailTimes.size() == kMaxTimestampCount) {
- guardrailTimes.pop_front();
- }
- guardrailTimes.push_back(timeSec);
-}
-
-void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) {
- noteDataDropped(key, totalBytes, getWallClockSec());
-}
-
-void StatsdStats::noteEventQueueOverflow(int64_t oldestEventTimestampNs) {
- lock_guard<std::mutex> lock(mLock);
-
- mOverflowCount++;
-
- int64_t history = getElapsedRealtimeNs() - oldestEventTimestampNs;
-
- if (history > mMaxQueueHistoryNs) {
- mMaxQueueHistoryNs = history;
- }
-
- if (history < mMinQueueHistoryNs) {
- mMinQueueHistoryNs = history;
- }
-}
-
-void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec) {
- lock_guard<std::mutex> lock(mLock);
- auto it = mConfigStats.find(key);
- if (it == mConfigStats.end()) {
- ALOGE("Config key %s not found!", key.ToString().c_str());
- return;
- }
- if (it->second->data_drop_time_sec.size() == kMaxTimestampCount) {
- it->second->data_drop_time_sec.pop_front();
- it->second->data_drop_bytes.pop_front();
- }
- it->second->data_drop_time_sec.push_back(timeSec);
- it->second->data_drop_bytes.push_back(totalBytes);
-}
-
-void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes) {
- noteMetricsReportSent(key, num_bytes, getWallClockSec());
-}
-
-void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes,
- int32_t timeSec) {
- lock_guard<std::mutex> lock(mLock);
- auto it = mConfigStats.find(key);
- if (it == mConfigStats.end()) {
- ALOGE("Config key %s not found!", key.ToString().c_str());
- return;
- }
- if (it->second->dump_report_stats.size() == kMaxTimestampCount) {
- it->second->dump_report_stats.pop_front();
- }
- it->second->dump_report_stats.push_back(std::make_pair(timeSec, num_bytes));
-}
-
-void StatsdStats::noteUidMapDropped(int deltas) {
- lock_guard<std::mutex> lock(mLock);
- mUidMapStats.dropped_changes += mUidMapStats.dropped_changes + deltas;
-}
-
-void StatsdStats::noteUidMapAppDeletionDropped() {
- lock_guard<std::mutex> lock(mLock);
- mUidMapStats.deleted_apps++;
-}
-
-void StatsdStats::setUidMapChanges(int changes) {
- lock_guard<std::mutex> lock(mLock);
- mUidMapStats.changes = changes;
-}
-
-void StatsdStats::setCurrentUidMapMemory(int bytes) {
- lock_guard<std::mutex> lock(mLock);
- mUidMapStats.bytes_used = bytes;
-}
-
-void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
- lock_guard<std::mutex> lock(mLock);
- // if name doesn't exist before, it will create the key with count 0.
- auto statsIt = mConfigStats.find(key);
- if (statsIt == mConfigStats.end()) {
- return;
- }
-
- auto& conditionSizeMap = statsIt->second->condition_stats;
- if (size > conditionSizeMap[id]) {
- conditionSizeMap[id] = size;
- }
-}
-
-void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
- lock_guard<std::mutex> lock(mLock);
- // if name doesn't exist before, it will create the key with count 0.
- auto statsIt = mConfigStats.find(key);
- if (statsIt == mConfigStats.end()) {
- return;
- }
- auto& metricsDimensionMap = statsIt->second->metric_stats;
- if (size > metricsDimensionMap[id]) {
- metricsDimensionMap[id] = size;
- }
-}
-
-void StatsdStats::noteMetricDimensionInConditionSize(
- const ConfigKey& key, const int64_t& id, int size) {
- lock_guard<std::mutex> lock(mLock);
- // if name doesn't exist before, it will create the key with count 0.
- auto statsIt = mConfigStats.find(key);
- if (statsIt == mConfigStats.end()) {
- return;
- }
- auto& metricsDimensionMap = statsIt->second->metric_dimension_in_condition_stats;
- if (size > metricsDimensionMap[id]) {
- metricsDimensionMap[id] = size;
- }
-}
-
-void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
- lock_guard<std::mutex> lock(mLock);
-
- auto statsIt = mConfigStats.find(key);
- if (statsIt == mConfigStats.end()) {
- return;
- }
- statsIt->second->matcher_stats[id]++;
-}
-
-void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) {
- lock_guard<std::mutex> lock(mLock);
- auto statsIt = mConfigStats.find(key);
- if (statsIt == mConfigStats.end()) {
- return;
- }
- statsIt->second->alert_stats[id]++;
-}
-
-void StatsdStats::noteRegisteredAnomalyAlarmChanged() {
- lock_guard<std::mutex> lock(mLock);
- mAnomalyAlarmRegisteredStats++;
-}
-
-void StatsdStats::noteRegisteredPeriodicAlarmChanged() {
- lock_guard<std::mutex> lock(mLock);
- mPeriodicAlarmRegisteredStats++;
-}
-
-void StatsdStats::updateMinPullIntervalSec(int pullAtomId, long intervalSec) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[pullAtomId].minPullIntervalSec =
- std::min(mPulledAtomStats[pullAtomId].minPullIntervalSec, intervalSec);
-}
-
-void StatsdStats::notePull(int pullAtomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[pullAtomId].totalPull++;
-}
-
-void StatsdStats::notePullFromCache(int pullAtomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[pullAtomId].totalPullFromCache++;
-}
-
-void StatsdStats::notePullTime(int pullAtomId, int64_t pullTimeNs) {
- lock_guard<std::mutex> lock(mLock);
- auto& pullStats = mPulledAtomStats[pullAtomId];
- pullStats.maxPullTimeNs = std::max(pullStats.maxPullTimeNs, pullTimeNs);
- pullStats.avgPullTimeNs = (pullStats.avgPullTimeNs * pullStats.numPullTime + pullTimeNs) /
- (pullStats.numPullTime + 1);
- pullStats.numPullTime += 1;
-}
-
-void StatsdStats::notePullDelay(int pullAtomId, int64_t pullDelayNs) {
- lock_guard<std::mutex> lock(mLock);
- auto& pullStats = mPulledAtomStats[pullAtomId];
- pullStats.maxPullDelayNs = std::max(pullStats.maxPullDelayNs, pullDelayNs);
- pullStats.avgPullDelayNs =
- (pullStats.avgPullDelayNs * pullStats.numPullDelay + pullDelayNs) /
- (pullStats.numPullDelay + 1);
- pullStats.numPullDelay += 1;
-}
-
-void StatsdStats::notePullDataError(int pullAtomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[pullAtomId].dataError++;
-}
-
-void StatsdStats::notePullTimeout(int pullAtomId,
- int64_t pullUptimeMillis,
- int64_t pullElapsedMillis) {
- lock_guard<std::mutex> lock(mLock);
- PulledAtomStats& pulledAtomStats = mPulledAtomStats[pullAtomId];
- pulledAtomStats.pullTimeout++;
-
- if (pulledAtomStats.pullTimeoutMetadata.size() == kMaxTimestampCount) {
- pulledAtomStats.pullTimeoutMetadata.pop_front();
- }
-
- pulledAtomStats.pullTimeoutMetadata.emplace_back(pullUptimeMillis, pullElapsedMillis);
-}
-
-void StatsdStats::notePullExceedMaxDelay(int pullAtomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[pullAtomId].pullExceedMaxDelay++;
-}
-
-void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
- lock_guard<std::mutex> lock(mLock);
-
- if (atomId <= kMaxPushedAtomId) {
- mPushedAtomStats[atomId]++;
- } else {
- if (mNonPlatformPushedAtomStats.size() < kMaxNonPlatformPushedAtoms) {
- mNonPlatformPushedAtomStats[atomId]++;
- }
- }
-}
-
-void StatsdStats::noteSystemServerRestart(int32_t timeSec) {
- lock_guard<std::mutex> lock(mLock);
-
- if (mSystemServerRestartSec.size() == kMaxSystemServerRestarts) {
- mSystemServerRestartSec.pop_front();
- }
- mSystemServerRestartSec.push_back(timeSec);
-}
-
-void StatsdStats::notePullFailed(int atomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].pullFailed++;
-}
-
-void StatsdStats::notePullUidProviderNotFound(int atomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].pullUidProviderNotFound++;
-}
-
-void StatsdStats::notePullerNotFound(int atomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].pullerNotFound++;
-}
-
-void StatsdStats::notePullBinderCallFailed(int atomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].binderCallFailCount++;
-}
-
-void StatsdStats::noteEmptyData(int atomId) {
- lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].emptyData++;
-}
-
-void StatsdStats::notePullerCallbackRegistrationChanged(int atomId, bool registered) {
- lock_guard<std::mutex> lock(mLock);
- if (registered) {
- mPulledAtomStats[atomId].registeredCount++;
- } else {
- mPulledAtomStats[atomId].unregisteredCount++;
- }
-}
-
-void StatsdStats::noteHardDimensionLimitReached(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).hardDimensionLimitReached++;
-}
-
-void StatsdStats::noteLateLogEventSkipped(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).lateLogEventSkipped++;
-}
-
-void StatsdStats::noteSkippedForwardBuckets(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).skippedForwardBuckets++;
-}
-
-void StatsdStats::noteBadValueType(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).badValueType++;
-}
-
-void StatsdStats::noteBucketDropped(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).bucketDropped++;
-}
-
-void StatsdStats::noteBucketUnknownCondition(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).bucketUnknownCondition++;
-}
-
-void StatsdStats::noteConditionChangeInNextBucket(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).conditionChangeInNextBucket++;
-}
-
-void StatsdStats::noteInvalidatedBucket(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).invalidatedBucket++;
-}
-
-void StatsdStats::noteBucketCount(int64_t metricId) {
- lock_guard<std::mutex> lock(mLock);
- getAtomMetricStats(metricId).bucketCount++;
-}
-
-void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs) {
- lock_guard<std::mutex> lock(mLock);
- AtomMetricStats& pullStats = getAtomMetricStats(metricId);
- pullStats.maxBucketBoundaryDelayNs =
- std::max(pullStats.maxBucketBoundaryDelayNs, timeDelayNs);
- pullStats.minBucketBoundaryDelayNs =
- std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
-}
-
-void StatsdStats::noteAtomError(int atomTag, bool pull) {
- lock_guard<std::mutex> lock(mLock);
- if (pull) {
- mPulledAtomStats[atomTag].atomErrorCount++;
- return;
- }
-
- bool present = (mPushedAtomErrorStats.find(atomTag) != mPushedAtomErrorStats.end());
- bool full = (mPushedAtomErrorStats.size() >= (size_t)kMaxPushedAtomErrorStatsSize);
- if (!full || present) {
- mPushedAtomErrorStats[atomTag]++;
- }
-}
-
-StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) {
- auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
- if (atomMetricStatsIter != mAtomMetricStats.end()) {
- return atomMetricStatsIter->second;
- }
- auto emplaceResult = mAtomMetricStats.emplace(metricId, AtomMetricStats());
- return emplaceResult.first->second;
-}
-
-void StatsdStats::reset() {
- lock_guard<std::mutex> lock(mLock);
- resetInternalLocked();
-}
-
-void StatsdStats::resetInternalLocked() {
- // Reset the historical data, but keep the active ConfigStats
- mStartTimeSec = getWallClockSec();
- mIceBox.clear();
- std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
- mNonPlatformPushedAtomStats.clear();
- mAnomalyAlarmRegisteredStats = 0;
- mPeriodicAlarmRegisteredStats = 0;
- mSystemServerRestartSec.clear();
- mLogLossStats.clear();
- mOverflowCount = 0;
- mMinQueueHistoryNs = kInt64Max;
- mMaxQueueHistoryNs = 0;
- for (auto& config : mConfigStats) {
- config.second->broadcast_sent_time_sec.clear();
- config.second->activation_time_sec.clear();
- config.second->deactivation_time_sec.clear();
- config.second->data_drop_time_sec.clear();
- config.second->data_drop_bytes.clear();
- config.second->dump_report_stats.clear();
- config.second->annotations.clear();
- config.second->matcher_stats.clear();
- config.second->condition_stats.clear();
- config.second->metric_stats.clear();
- config.second->metric_dimension_in_condition_stats.clear();
- config.second->alert_stats.clear();
- }
- for (auto& pullStats : mPulledAtomStats) {
- pullStats.second.totalPull = 0;
- pullStats.second.totalPullFromCache = 0;
- pullStats.second.minPullIntervalSec = LONG_MAX;
- pullStats.second.avgPullTimeNs = 0;
- pullStats.second.maxPullTimeNs = 0;
- pullStats.second.numPullTime = 0;
- pullStats.second.avgPullDelayNs = 0;
- pullStats.second.maxPullDelayNs = 0;
- pullStats.second.numPullDelay = 0;
- pullStats.second.dataError = 0;
- pullStats.second.pullTimeout = 0;
- pullStats.second.pullExceedMaxDelay = 0;
- pullStats.second.pullFailed = 0;
- pullStats.second.pullUidProviderNotFound = 0;
- pullStats.second.pullerNotFound = 0;
- pullStats.second.registeredCount = 0;
- pullStats.second.unregisteredCount = 0;
- pullStats.second.atomErrorCount = 0;
- pullStats.second.binderCallFailCount = 0;
- pullStats.second.pullTimeoutMetadata.clear();
- }
- mAtomMetricStats.clear();
- mActivationBroadcastGuardrailStats.clear();
- mPushedAtomErrorStats.clear();
-}
-
-string buildTimeString(int64_t timeSec) {
- time_t t = timeSec;
- struct tm* tm = localtime(&t);
- char timeBuffer[80];
- strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm);
- return string(timeBuffer);
-}
-
-int StatsdStats::getPushedAtomErrors(int atomId) const {
- const auto& it = mPushedAtomErrorStats.find(atomId);
- if (it != mPushedAtomErrorStats.end()) {
- return it->second;
- } else {
- return 0;
- }
-}
-
-void StatsdStats::dumpStats(int out) const {
- lock_guard<std::mutex> lock(mLock);
- time_t t = mStartTimeSec;
- struct tm* tm = localtime(&t);
- char timeBuffer[80];
- strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm);
- dprintf(out, "Stats collection start second: %s\n", timeBuffer);
- dprintf(out, "%lu Config in icebox: \n", (unsigned long)mIceBox.size());
- for (const auto& configStats : mIceBox) {
- dprintf(out,
- "Config {%d_%lld}: creation=%d, deletion=%d, reset=%d, #metric=%d, #condition=%d, "
- "#matcher=%d, #alert=%d, valid=%d\n",
- configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
- configStats->deletion_time_sec, configStats->reset_time_sec,
- configStats->metric_count, configStats->condition_count, configStats->matcher_count,
- configStats->alert_count, configStats->is_valid);
-
- for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
- dprintf(out, "\tbroadcast time: %d\n", broadcastTime);
- }
-
- for (const int& activationTime : configStats->activation_time_sec) {
- dprintf(out, "\tactivation time: %d\n", activationTime);
- }
-
- for (const int& deactivationTime : configStats->deactivation_time_sec) {
- dprintf(out, "\tdeactivation time: %d\n", deactivationTime);
- }
-
- auto dropTimePtr = configStats->data_drop_time_sec.begin();
- auto dropBytesPtr = configStats->data_drop_bytes.begin();
- for (int i = 0; i < (int)configStats->data_drop_time_sec.size();
- i++, dropTimePtr++, dropBytesPtr++) {
- dprintf(out, "\tdata drop time: %d with size %lld", *dropTimePtr,
- (long long)*dropBytesPtr);
- }
- }
- dprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size());
- for (auto& pair : mConfigStats) {
- auto& configStats = pair.second;
- dprintf(out,
- "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
- "#matcher=%d, #alert=%d, valid=%d\n",
- configStats->uid, (long long)configStats->id, configStats->creation_time_sec,
- configStats->deletion_time_sec, configStats->metric_count,
- configStats->condition_count, configStats->matcher_count, configStats->alert_count,
- configStats->is_valid);
- for (const auto& annotation : configStats->annotations) {
- dprintf(out, "\tannotation: %lld, %d\n", (long long)annotation.first,
- annotation.second);
- }
-
- for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
- dprintf(out, "\tbroadcast time: %s(%lld)\n", buildTimeString(broadcastTime).c_str(),
- (long long)broadcastTime);
- }
-
- for (const int& activationTime : configStats->activation_time_sec) {
- dprintf(out, "\tactivation time: %d\n", activationTime);
- }
-
- for (const int& deactivationTime : configStats->deactivation_time_sec) {
- dprintf(out, "\tdeactivation time: %d\n", deactivationTime);
- }
-
- auto dropTimePtr = configStats->data_drop_time_sec.begin();
- auto dropBytesPtr = configStats->data_drop_bytes.begin();
- for (int i = 0; i < (int)configStats->data_drop_time_sec.size();
- i++, dropTimePtr++, dropBytesPtr++) {
- dprintf(out, "\tdata drop time: %s(%lld) with %lld bytes\n",
- buildTimeString(*dropTimePtr).c_str(), (long long)*dropTimePtr,
- (long long)*dropBytesPtr);
- }
-
- for (const auto& dump : configStats->dump_report_stats) {
- dprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
- buildTimeString(dump.first).c_str(), (long long)dump.first,
- (long long)dump.second);
- }
-
- for (const auto& stats : pair.second->matcher_stats) {
- dprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second);
- }
-
- for (const auto& stats : pair.second->condition_stats) {
- dprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first,
- stats.second);
- }
-
- for (const auto& stats : pair.second->condition_stats) {
- dprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first,
- stats.second);
- }
-
- for (const auto& stats : pair.second->alert_stats) {
- dprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second);
- }
- }
- dprintf(out, "********Disk Usage stats***********\n");
- StorageManager::printStats(out);
- dprintf(out, "********Pushed Atom stats***********\n");
- const size_t atomCounts = mPushedAtomStats.size();
- for (size_t i = 2; i < atomCounts; i++) {
- if (mPushedAtomStats[i] > 0) {
- dprintf(out, "Atom %zu->(total count)%d, (error count)%d\n", i, mPushedAtomStats[i],
- getPushedAtomErrors((int)i));
- }
- }
- for (const auto& pair : mNonPlatformPushedAtomStats) {
- dprintf(out, "Atom %d->(total count)%d, (error count)%d\n", pair.first, pair.second,
- getPushedAtomErrors(pair.first));
- }
-
- dprintf(out, "********Pulled Atom stats***********\n");
- for (const auto& pair : mPulledAtomStats) {
- dprintf(out,
- "Atom %d->(total pull)%ld, (pull from cache)%ld, "
- "(pull failed)%ld, (min pull interval)%ld \n"
- " (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
- "nanos)%lld, "
- " (max pull delay nanos)%lld, (data error)%ld\n"
- " (pull timeout)%ld, (pull exceed max delay)%ld"
- " (no uid provider count)%ld, (no puller found count)%ld\n"
- " (registered count) %ld, (unregistered count) %ld"
- " (atom error count) %d\n",
- (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
- (long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec,
- (long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs,
- (long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs,
- pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
- pair.second.pullUidProviderNotFound, pair.second.pullerNotFound,
- pair.second.registeredCount, pair.second.unregisteredCount,
- pair.second.atomErrorCount);
- if (pair.second.pullTimeoutMetadata.size() > 0) {
- string uptimeMillis = "(pull timeout system uptime millis) ";
- string pullTimeoutMillis = "(pull timeout elapsed time millis) ";
- for (const auto& stats : pair.second.pullTimeoutMetadata) {
- uptimeMillis.append(to_string(stats.pullTimeoutUptimeMillis)).append(",");;
- pullTimeoutMillis.append(to_string(stats.pullTimeoutElapsedMillis)).append(",");
- }
- uptimeMillis.pop_back();
- uptimeMillis.push_back('\n');
- pullTimeoutMillis.pop_back();
- pullTimeoutMillis.push_back('\n');
- dprintf(out, "%s", uptimeMillis.c_str());
- dprintf(out, "%s", pullTimeoutMillis.c_str());
- }
- }
-
- if (mAnomalyAlarmRegisteredStats > 0) {
- dprintf(out, "********AnomalyAlarmStats stats***********\n");
- dprintf(out, "Anomaly alarm registrations: %d\n", mAnomalyAlarmRegisteredStats);
- }
-
- if (mPeriodicAlarmRegisteredStats > 0) {
- dprintf(out, "********SubscriberAlarmStats stats***********\n");
- dprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
- }
-
- dprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n",
- mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps,
- mUidMapStats.dropped_changes);
-
- for (const auto& restart : mSystemServerRestartSec) {
- dprintf(out, "System server restarts at %s(%lld)\n", buildTimeString(restart).c_str(),
- (long long)restart);
- }
-
- for (const auto& loss : mLogLossStats) {
- dprintf(out,
- "Log loss: %lld (wall clock sec) - %d (count), %d (last error), %d (last tag), %d "
- "(uid), %d (pid)\n",
- (long long)loss.mWallClockSec, loss.mCount, loss.mLastError, loss.mLastTag,
- loss.mUid, loss.mPid);
- }
-
- dprintf(out, "Event queue overflow: %d; MaxHistoryNs: %lld; MinHistoryNs: %lld\n",
- mOverflowCount, (long long)mMaxQueueHistoryNs, (long long)mMinQueueHistoryNs);
-
- if (mActivationBroadcastGuardrailStats.size() > 0) {
- dprintf(out, "********mActivationBroadcastGuardrail stats***********\n");
- for (const auto& pair: mActivationBroadcastGuardrailStats) {
- dprintf(out, "Uid %d: Times: ", pair.first);
- for (const auto& guardrailHitTime : pair.second) {
- dprintf(out, "%d ", guardrailHitTime);
- }
- }
- dprintf(out, "\n");
- }
-}
-
-void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) {
- uint64_t token =
- proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_UID, configStats.uid);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ID, (long long)configStats.id);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CREATION, configStats.creation_time_sec);
- if (configStats.reset_time_sec != 0) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_RESET, configStats.reset_time_sec);
- }
- if (configStats.deletion_time_sec != 0) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DELETION,
- configStats.deletion_time_sec);
- }
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_METRIC_COUNT, configStats.metric_count);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CONDITION_COUNT,
- configStats.condition_count);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_MATCHER_COUNT, configStats.matcher_count);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ALERT_COUNT, configStats.alert_count);
- proto->write(FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_VALID, configStats.is_valid);
-
- for (const auto& broadcast : configStats.broadcast_sent_time_sec) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_BROADCAST | FIELD_COUNT_REPEATED,
- broadcast);
- }
-
- for (const auto& activation : configStats.activation_time_sec) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ACTIVATION | FIELD_COUNT_REPEATED,
- activation);
- }
-
- for (const auto& deactivation : configStats.deactivation_time_sec) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DEACTIVATION | FIELD_COUNT_REPEATED,
- deactivation);
- }
-
- for (const auto& drop_time : configStats.data_drop_time_sec) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP_TIME | FIELD_COUNT_REPEATED,
- drop_time);
- }
-
- for (const auto& drop_bytes : configStats.data_drop_bytes) {
- proto->write(
- FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DATA_DROP_BYTES | FIELD_COUNT_REPEATED,
- (long long)drop_bytes);
- }
-
- for (const auto& dump : configStats.dump_report_stats) {
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME |
- FIELD_COUNT_REPEATED,
- dump.first);
- }
-
- for (const auto& dump : configStats.dump_report_stats) {
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES |
- FIELD_COUNT_REPEATED,
- (long long)dump.second);
- }
-
- for (const auto& annotation : configStats.annotations) {
- uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_CONFIG_STATS_ANNOTATION);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ANNOTATION_INT64,
- (long long)annotation.first);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ANNOTATION_INT32, annotation.second);
- proto->end(token);
- }
-
- for (const auto& pair : configStats.matcher_stats) {
- uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_CONFIG_STATS_MATCHER_STATS);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_MATCHER_STATS_ID, (long long)pair.first);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_MATCHER_STATS_COUNT, pair.second);
- proto->end(tmpToken);
- }
-
- for (const auto& pair : configStats.condition_stats) {
- uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_CONFIG_STATS_CONDITION_STATS);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_STATS_ID, (long long)pair.first);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONDITION_STATS_COUNT, pair.second);
- proto->end(tmpToken);
- }
-
- for (const auto& pair : configStats.metric_stats) {
- uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_CONFIG_STATS_METRIC_STATS);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second);
- proto->end(tmpToken);
- }
- for (const auto& pair : configStats.metric_dimension_in_condition_stats) {
- uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_CONFIG_STATS_METRIC_DIMENSION_IN_CONDITION_STATS);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second);
- proto->end(tmpToken);
- }
-
- for (const auto& pair : configStats.alert_stats) {
- uint64_t tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_CONFIG_STATS_ALERT_STATS);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_STATS_ID, (long long)pair.first);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_ALERT_STATS_COUNT, pair.second);
- proto->end(tmpToken);
- }
-
- proto->end(token);
-}
-
-void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
- lock_guard<std::mutex> lock(mLock);
-
- ProtoOutputStream proto;
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec());
-
- for (const auto& configStats : mIceBox) {
- addConfigStatsToProto(*configStats, &proto);
- }
-
- for (auto& pair : mConfigStats) {
- addConfigStatsToProto(*(pair.second), &proto);
- }
-
- const size_t atomCounts = mPushedAtomStats.size();
- for (size_t i = 2; i < atomCounts; i++) {
- if (mPushedAtomStats[i] > 0) {
- uint64_t token =
- proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
- int errors = getPushedAtomErrors(i);
- if (errors > 0) {
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
- }
- proto.end(token);
- }
- }
-
- for (const auto& pair : mNonPlatformPushedAtomStats) {
- uint64_t token =
- proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second);
- int errors = getPushedAtomErrors(pair.first);
- if (errors > 0) {
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
- }
- proto.end(token);
- }
-
- for (const auto& pair : mPulledAtomStats) {
- android::os::statsd::writePullerStatsToStream(pair, &proto);
- }
-
- for (const auto& pair : mAtomMetricStats) {
- android::os::statsd::writeAtomMetricStatsToStream(pair, &proto);
- }
-
- if (mAnomalyAlarmRegisteredStats > 0) {
- uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED,
- mAnomalyAlarmRegisteredStats);
- proto.end(token);
- }
-
- if (mPeriodicAlarmRegisteredStats > 0) {
- uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PERIODIC_ALARM_STATS);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_PERIODIC_ALARMS_REGISTERED,
- mPeriodicAlarmRegisteredStats);
- proto.end(token);
- }
-
- uint64_t uidMapToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_CHANGES, mUidMapStats.changes);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_BYTES_USED, mUidMapStats.bytes_used);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_CHANGES, mUidMapStats.dropped_changes);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DELETED_APPS, mUidMapStats.deleted_apps);
- proto.end(uidMapToken);
-
- for (const auto& error : mLogLossStats) {
- uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS |
- FIELD_COUNT_REPEATED);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TIME, error.mWallClockSec);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_COUNT, error.mCount);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_ERROR, error.mLastError);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TAG, error.mLastTag);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_UID, error.mUid);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_PID, error.mPid);
- proto.end(token);
- }
-
- if (mOverflowCount > 0) {
- uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_OVERFLOW);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_OVERFLOW_COUNT, (int32_t)mOverflowCount);
- proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MAX_HISTORY,
- (long long)mMaxQueueHistoryNs);
- proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MIN_HISTORY,
- (long long)mMinQueueHistoryNs);
- proto.end(token);
- }
-
- for (const auto& restart : mSystemServerRestartSec) {
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_SYSTEM_SERVER_RESTART | FIELD_COUNT_REPEATED,
- restart);
- }
-
- for (const auto& pair: mActivationBroadcastGuardrailStats) {
- uint64_t token = proto.start(FIELD_TYPE_MESSAGE |
- FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL |
- FIELD_COUNT_REPEATED);
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_UID,
- (int32_t) pair.first);
- for (const auto& guardrailHitTime : pair.second) {
- proto.write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME |
- FIELD_COUNT_REPEATED,
- guardrailHitTime);
- }
- proto.end(token);
- }
-
- output->clear();
- size_t bufferSize = proto.size();
- output->resize(bufferSize);
-
- size_t pos = 0;
- sp<android::util::ProtoReader> reader = proto.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((*output)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- if (reset) {
- resetInternalLocked();
- }
-
- VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
deleted file mode 100644
index 005048446fc3..000000000000
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ /dev/null
@@ -1,678 +0,0 @@
-/*
- * Copyright 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.
- */
-#pragma once
-
-#include "config/ConfigKey.h"
-
-#include <gtest/gtest_prod.h>
-#include <log/log_time.h>
-#include <list>
-#include <mutex>
-#include <string>
-#include <vector>
-#include <unordered_map>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-struct ConfigStats {
- int32_t uid;
- int64_t id;
- int32_t creation_time_sec;
- int32_t deletion_time_sec = 0;
- int32_t reset_time_sec = 0;
- int32_t metric_count;
- int32_t condition_count;
- int32_t matcher_count;
- int32_t alert_count;
- bool is_valid;
-
- std::list<int32_t> broadcast_sent_time_sec;
-
- // Times at which this config is activated.
- std::list<int32_t> activation_time_sec;
-
- // Times at which this config is deactivated.
- std::list<int32_t> deactivation_time_sec;
-
- std::list<int32_t> data_drop_time_sec;
- // Number of bytes dropped at corresponding time.
- std::list<int64_t> data_drop_bytes;
- std::list<std::pair<int32_t, int64_t>> dump_report_stats;
-
- // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
- std::map<const int64_t, int> matcher_stats;
-
- // Stores the number of output tuple of condition trackers when it's bigger than
- // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
- // it means some data has been dropped. The map size is capped by kMaxConfigCount.
- std::map<const int64_t, int> condition_stats;
-
- // Stores the number of output tuple of metric producers when it's bigger than
- // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
- // it means some data has been dropped. The map size is capped by kMaxConfigCount.
- std::map<const int64_t, int> metric_stats;
-
- // Stores the max number of output tuple of dimensions in condition across dimensions in what
- // when it's bigger than kDimensionKeySizeSoftLimit. When you see the number is
- // kDimensionKeySizeHardLimit +1, it means some data has been dropped. The map size is capped by
- // kMaxConfigCount.
- std::map<const int64_t, int> metric_dimension_in_condition_stats;
-
- // Stores the number of times an anomaly detection alert has been declared.
- // The map size is capped by kMaxConfigCount.
- std::map<const int64_t, int> alert_stats;
-
- // Stores the config ID for each sub-config used.
- std::list<std::pair<const int64_t, const int32_t>> annotations;
-};
-
-struct UidMapStats {
- int32_t changes;
- int32_t bytes_used;
- int32_t dropped_changes;
- int32_t deleted_apps = 0;
-};
-
-// Keeps track of stats of statsd.
-// Single instance shared across the process. All public methods are thread safe.
-class StatsdStats {
-public:
- static StatsdStats& getInstance();
- ~StatsdStats(){};
-
- const static int kDimensionKeySizeSoftLimit = 500;
- const static int kDimensionKeySizeHardLimit = 800;
-
- // Per atom dimension key size limit
- static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
-
- const static int kMaxConfigCountPerUid = 20;
- const static int kMaxAlertCountPerConfig = 100;
- const static int kMaxConditionCountPerConfig = 300;
- const static int kMaxMetricCountPerConfig = 1000;
- const static int kMaxMatcherCountPerConfig = 800;
-
- // The max number of old config stats we keep.
- const static int kMaxIceBoxSize = 20;
-
- const static int kMaxLoggerErrors = 20;
-
- const static int kMaxSystemServerRestarts = 20;
-
- const static int kMaxTimestampCount = 20;
-
- const static int kMaxLogSourceCount = 50;
-
- const static int kMaxPullAtomPackages = 100;
-
- // Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
- // drops the metrics data in memory.
- static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
-
- // Soft memory limit per configuration. Once this limit is exceeded, we begin notifying the
- // data subscriber that it's time to call getData.
- static const size_t kBytesPerConfigTriggerGetData = 192 * 1024;
-
- // Cap the UID map's memory usage to this. This should be fairly high since the UID information
- // is critical for understanding the metrics.
- const static size_t kMaxBytesUsedUidMap = 50 * 1024;
-
- // The number of deleted apps that are stored in the uid map.
- const static int kMaxDeletedAppsInUidMap = 100;
-
- /* Minimum period between two broadcasts in nanoseconds. */
- static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
-
- /* Min period between two checks of byte size per config key in nanoseconds. */
- static const int64_t kMinByteSizeCheckPeriodNs = 60 * NS_PER_SEC;
-
- /* Minimum period between two activation broadcasts in nanoseconds. */
- static const int64_t kMinActivationBroadcastPeriodNs = 10 * NS_PER_SEC;
-
- // Maximum age (30 days) that files on disk can exist in seconds.
- static const int kMaxAgeSecond = 60 * 60 * 24 * 30;
-
- // Maximum age (2 days) that local history files on disk can exist in seconds.
- static const int kMaxLocalHistoryAgeSecond = 60 * 60 * 24 * 2;
-
- // Maximum number of files (1000) that can be in stats directory on disk.
- static const int kMaxFileNumber = 1000;
-
- // Maximum size of all files that can be written to stats directory on disk.
- static const int kMaxFileSize = 50 * 1024 * 1024;
-
- // How long to try to clear puller cache from last time
- static const long kPullerCacheClearIntervalSec = 1;
-
- // Max time to do a pull.
- static const int64_t kPullMaxDelayNs = 30 * NS_PER_SEC;
-
- // Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
- static const int kMaxNonPlatformPushedAtoms = 100;
-
- // Maximum atom id value that we consider a platform pushed atom.
- // This should be updated once highest pushed atom id in atoms.proto approaches this value.
- static const int kMaxPushedAtomId = 500;
-
- // Atom id that is the start of the pulled atoms.
- static const int kPullAtomStartTag = 10000;
-
- // Atom id that is the start of vendor atoms.
- static const int kVendorAtomStartTag = 100000;
-
- // Vendor pulled atom start id.
- static const int32_t kVendorPulledAtomStartTag = 150000;
-
- // Beginning of range for timestamp truncation.
- static const int32_t kTimestampTruncationStartTag = 300000;
-
- // End of range for timestamp truncation.
- static const int32_t kTimestampTruncationEndTag = 304999;
-
- // Max accepted atom id.
- static const int32_t kMaxAtomTag = 200000;
-
- static const int64_t kInt64Max = 0x7fffffffffffffffLL;
-
- static const int32_t kMaxLoggedBucketDropEvents = 10;
-
- /**
- * Report a new config has been received and report the static stats about the config.
- *
- * The static stats include: the count of metrics, conditions, matchers, and alerts.
- * If the config is not valid, this config stats will be put into icebox immediately.
- */
- void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
- int matchersCount, int alertCount,
- const std::list<std::pair<const int64_t, const int32_t>>& annotations,
- bool isValid);
- /**
- * Report a config has been removed.
- */
- void noteConfigRemoved(const ConfigKey& key);
- /**
- * Report a config has been reset when ttl expires.
- */
- void noteConfigReset(const ConfigKey& key);
-
- /**
- * Report a broadcast has been sent to a config owner to collect the data.
- */
- void noteBroadcastSent(const ConfigKey& key);
-
- /**
- * Report that a config has become activated or deactivated.
- * This can be different from whether or not a broadcast is sent if the
- * guardrail prevented the broadcast from being sent.
- */
- void noteActiveStatusChanged(const ConfigKey& key, bool activate);
-
- /**
- * Report a config's metrics data has been dropped.
- */
- void noteDataDropped(const ConfigKey& key, const size_t totalBytes);
-
- /**
- * Report metrics data report has been sent.
- *
- * The report may be requested via StatsManager API, or through adb cmd.
- */
- void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes);
-
- /**
- * Report the size of output tuple of a condition.
- *
- * Note: only report when the condition has an output dimension, and the tuple
- * count > kDimensionKeySizeSoftLimit.
- *
- * [key]: The config key that this condition belongs to.
- * [id]: The id of the condition.
- * [size]: The output tuple size.
- */
- void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size);
-
- /**
- * Report the size of output tuple of a metric.
- *
- * Note: only report when the metric has an output dimension, and the tuple
- * count > kDimensionKeySizeSoftLimit.
- *
- * [key]: The config key that this metric belongs to.
- * [id]: The id of the metric.
- * [size]: The output tuple size.
- */
- void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
-
- /**
- * Report the max size of output tuple of dimension in condition across dimensions in what.
- *
- * Note: only report when the metric has an output dimension in condition, and the max tuple
- * count > kDimensionKeySizeSoftLimit.
- *
- * [key]: The config key that this metric belongs to.
- * [id]: The id of the metric.
- * [size]: The output tuple size.
- */
- void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size);
-
- /**
- * Report a matcher has been matched.
- *
- * [key]: The config key that this matcher belongs to.
- * [id]: The id of the matcher.
- */
- void noteMatcherMatched(const ConfigKey& key, const int64_t& id);
-
- /**
- * Report that an anomaly detection alert has been declared.
- *
- * [key]: The config key that this alert belongs to.
- * [id]: The id of the alert.
- */
- void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id);
-
- /**
- * Report an atom event has been logged.
- */
- void noteAtomLogged(int atomId, int32_t timeSec);
-
- /**
- * Report that statsd modified the anomaly alarm registered with StatsCompanionService.
- */
- void noteRegisteredAnomalyAlarmChanged();
-
- /**
- * Report that statsd modified the periodic alarm registered with StatsCompanionService.
- */
- void noteRegisteredPeriodicAlarmChanged();
-
- /**
- * Records the number of delta entries that are being dropped from the uid map.
- */
- void noteUidMapDropped(int deltas);
-
- /**
- * Records that an app was deleted (from statsd's map).
- */
- void noteUidMapAppDeletionDropped();
-
- /**
- * Updates the number of changes currently stored in the uid map.
- */
- void setUidMapChanges(int changes);
- void setCurrentUidMapMemory(int bytes);
-
- /*
- * Updates minimum interval between pulls for an pulled atom.
- */
- void updateMinPullIntervalSec(int pullAtomId, long intervalSec);
-
- /*
- * Notes an atom is pulled.
- */
- void notePull(int pullAtomId);
-
- /*
- * Notes an atom is served from puller cache.
- */
- void notePullFromCache(int pullAtomId);
-
- /*
- * Notify data error for pulled atom.
- */
- void notePullDataError(int pullAtomId);
-
- /*
- * Records time for actual pulling, not including those served from cache and not including
- * statsd processing delays.
- */
- void notePullTime(int pullAtomId, int64_t pullTimeNs);
-
- /*
- * Records pull delay for a pulled atom, including those served from cache and including statsd
- * processing delays.
- */
- void notePullDelay(int pullAtomId, int64_t pullDelayNs);
-
- /*
- * Records pull exceeds timeout for the puller.
- */
- void notePullTimeout(int pullAtomId, int64_t pullUptimeMillis, int64_t pullElapsedMillis);
-
- /*
- * Records pull exceeds max delay for a metric.
- */
- void notePullExceedMaxDelay(int pullAtomId);
-
- /*
- * Records when system server restarts.
- */
- void noteSystemServerRestart(int32_t timeSec);
-
- /**
- * Records statsd skipped an event.
- */
- void noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError,
- int32_t lastAtomTag, int32_t uid, int32_t pid);
-
- /**
- * Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if
- * the pull timed out, or if the outgoing binder call failed.
- * This count will only increment if the puller was actually invoked.
- *
- * It does not include a pull not occurring due to not finding the appropriate
- * puller. These cases are covered in other counts.
- */
- void notePullFailed(int atomId);
-
- /**
- * Records that the pull of an atom has failed due to not having a uid provider.
- */
- void notePullUidProviderNotFound(int atomId);
-
- /**
- * Records that the pull of an atom has failed due not finding a puller registered by a
- * trusted uid.
- */
- void notePullerNotFound(int atomId);
-
- /**
- * Records that the pull has failed due to the outgoing binder call failing.
- */
- void notePullBinderCallFailed(int atomId);
-
- /**
- * A pull with no data occurred
- */
- void noteEmptyData(int atomId);
-
- /**
- * Records that a puller callback for the given atomId was registered or unregistered.
- *
- * @param registered True if the callback was registered, false if was unregistered.
- */
- void notePullerCallbackRegistrationChanged(int atomId, bool registered);
-
- /**
- * Hard limit was reached in the cardinality of an atom
- */
- void noteHardDimensionLimitReached(int64_t metricId);
-
- /**
- * A log event was too late, arrived in the wrong bucket and was skipped
- */
- void noteLateLogEventSkipped(int64_t metricId);
-
- /**
- * Buckets were skipped as time elapsed without any data for them
- */
- void noteSkippedForwardBuckets(int64_t metricId);
-
- /**
- * An unsupported value type was received
- */
- void noteBadValueType(int64_t metricId);
-
- /**
- * Buckets were dropped due to reclaim memory.
- */
- void noteBucketDropped(int64_t metricId);
-
- /**
- * A condition change was too late, arrived in the wrong bucket and was skipped
- */
- void noteConditionChangeInNextBucket(int64_t metricId);
-
- /**
- * A bucket has been tagged as invalid.
- */
- void noteInvalidatedBucket(int64_t metricId);
-
- /**
- * Tracks the total number of buckets (include skipped/invalid buckets).
- */
- void noteBucketCount(int64_t metricId);
-
- /**
- * For pulls at bucket boundaries, it represents the misalignment between the real timestamp and
- * the end of the bucket.
- */
- void noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs);
-
- /**
- * Number of buckets with unknown condition.
- */
- void noteBucketUnknownCondition(int64_t metricId);
-
- /* Reports one event has been dropped due to queue overflow, and the oldest event timestamp in
- * the queue */
- void noteEventQueueOverflow(int64_t oldestEventTimestampNs);
-
- /**
- * Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast
- * should have been sent, but instead was skipped due to hitting the guardrail.
- */
- void noteActivationBroadcastGuardrailHit(const int uid);
-
- /**
- * Reports that an atom is erroneous or cannot be parsed successfully by
- * statsd. An atom tag of 0 indicates that the client did not supply the
- * atom id within the encoding.
- *
- * For pushed atoms only, this call should be preceded by a call to
- * noteAtomLogged.
- */
- void noteAtomError(int atomTag, bool pull=false);
-
- /**
- * Reset the historical stats. Including all stats in icebox, and the tracked stats about
- * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
- * to collect stats after reset() has been called.
- */
- void reset();
-
- /**
- * Output the stats in protobuf binary format to [buffer].
- *
- * [reset]: whether to clear the historical stats after the call.
- */
- void dumpStats(std::vector<uint8_t>* buffer, bool reset);
-
- /**
- * Output statsd stats in human readable format to [out] file descriptor.
- */
- void dumpStats(int outFd) const;
-
- typedef struct PullTimeoutMetadata {
- int64_t pullTimeoutUptimeMillis;
- int64_t pullTimeoutElapsedMillis;
- PullTimeoutMetadata(int64_t uptimeMillis, int64_t elapsedMillis) :
- pullTimeoutUptimeMillis(uptimeMillis),
- pullTimeoutElapsedMillis(elapsedMillis) {/* do nothing */}
- } PullTimeoutMetadata;
-
- typedef struct {
- long totalPull = 0;
- long totalPullFromCache = 0;
- long minPullIntervalSec = LONG_MAX;
- int64_t avgPullTimeNs = 0;
- int64_t maxPullTimeNs = 0;
- long numPullTime = 0;
- int64_t avgPullDelayNs = 0;
- int64_t maxPullDelayNs = 0;
- long numPullDelay = 0;
- long dataError = 0;
- long pullTimeout = 0;
- long pullExceedMaxDelay = 0;
- long pullFailed = 0;
- long pullUidProviderNotFound = 0;
- long pullerNotFound = 0;
- long emptyData = 0;
- long registeredCount = 0;
- long unregisteredCount = 0;
- int32_t atomErrorCount = 0;
- long binderCallFailCount = 0;
- std::list<PullTimeoutMetadata> pullTimeoutMetadata;
- } PulledAtomStats;
-
- typedef struct {
- long hardDimensionLimitReached = 0;
- long lateLogEventSkipped = 0;
- long skippedForwardBuckets = 0;
- long badValueType = 0;
- long conditionChangeInNextBucket = 0;
- long invalidatedBucket = 0;
- long bucketDropped = 0;
- int64_t minBucketBoundaryDelayNs = 0;
- int64_t maxBucketBoundaryDelayNs = 0;
- long bucketUnknownCondition = 0;
- long bucketCount = 0;
- } AtomMetricStats;
-
-private:
- StatsdStats();
-
- mutable std::mutex mLock;
-
- int32_t mStartTimeSec;
-
- // Track the number of dropped entries used by the uid map.
- UidMapStats mUidMapStats;
-
- // The stats about the configs that are still in use.
- // The map size is capped by kMaxConfigCount.
- std::map<const ConfigKey, std::shared_ptr<ConfigStats>> mConfigStats;
-
- // Stores the stats for the configs that are no longer in use.
- // The size of the vector is capped by kMaxIceBoxSize.
- std::list<const std::shared_ptr<ConfigStats>> mIceBox;
-
- // Stores the number of times a pushed atom is logged.
- // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
- // out of that range will be put in mNonPlatformPushedAtomStats.
- // This is a vector, not a map because it will be accessed A LOT -- for each stats log.
- std::vector<int> mPushedAtomStats;
-
- // Stores the number of times a pushed atom is logged for atom ids above kMaxPushedAtomId.
- // The max size of the map is kMaxNonPlatformPushedAtoms.
- std::unordered_map<int, int> mNonPlatformPushedAtomStats;
-
- // Maps PullAtomId to its stats. The size is capped by the puller atom counts.
- std::map<int, PulledAtomStats> mPulledAtomStats;
-
- // Stores the number of times a pushed atom was logged erroneously. The
- // corresponding counts for pulled atoms are stored in PulledAtomStats.
- // The max size of this map is kMaxAtomErrorsStatsSize.
- std::map<int, int> mPushedAtomErrorStats;
- int kMaxPushedAtomErrorStatsSize = 100;
-
- // Maps metric ID to its stats. The size is capped by the number of metrics.
- std::map<int64_t, AtomMetricStats> mAtomMetricStats;
-
- // Maps uids to times when the activation changed broadcast not sent due to hitting the
- // guardrail. The size is capped by the number of configs, and up to 20 times per uid.
- std::map<int, std::list<int32_t>> mActivationBroadcastGuardrailStats;
-
- struct LogLossStats {
- LogLossStats(int32_t sec, int32_t count, int32_t error, int32_t tag, int32_t uid,
- int32_t pid)
- : mWallClockSec(sec),
- mCount(count),
- mLastError(error),
- mLastTag(tag),
- mUid(uid),
- mPid(pid) {
- }
- int32_t mWallClockSec;
- int32_t mCount;
- // error code defined in linux/errno.h
- int32_t mLastError;
- int32_t mLastTag;
- int32_t mUid;
- int32_t mPid;
- };
-
- // Max of {(now - oldestEventTimestamp) when overflow happens}.
- // This number is helpful to understand how SLOW statsd can be.
- int64_t mMaxQueueHistoryNs = 0;
-
- // Min of {(now - oldestEventTimestamp) when overflow happens}.
- // This number is helpful to understand how FAST the events floods to statsd.
- int64_t mMinQueueHistoryNs = kInt64Max;
-
- // Total number of events that are lost due to queue overflow.
- int32_t mOverflowCount = 0;
-
- // Timestamps when we detect log loss, and the number of logs lost.
- std::list<LogLossStats> mLogLossStats;
-
- std::list<int32_t> mSystemServerRestartSec;
-
- // Stores the number of times statsd modified the anomaly alarm registered with
- // StatsCompanionService.
- int mAnomalyAlarmRegisteredStats = 0;
-
- // Stores the number of times statsd registers the periodic alarm changes
- int mPeriodicAlarmRegisteredStats = 0;
-
- void noteConfigResetInternalLocked(const ConfigKey& key);
-
- void noteConfigRemovedInternalLocked(const ConfigKey& key);
-
- void resetInternalLocked();
-
- void noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec);
-
- void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec);
-
- void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
-
- void noteActiveStatusChanged(const ConfigKey& key, bool activate, int32_t timeSec);
-
- void noteActivationBroadcastGuardrailHit(const int uid, int32_t timeSec);
-
- void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
-
- int getPushedAtomErrors(int atomId) const;
-
- /**
- * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
- * will live as long as `this`.
- */
- StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
-
- FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
- FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
- FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
- FRIEND_TEST(StatsdStatsTest, TestSubStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomLog);
- FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog);
- FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
- FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
- FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
- FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
- FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
- FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
- FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
-
- FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/hash.cpp b/cmds/statsd/src/hash.cpp
deleted file mode 100644
index 543a748adedb..000000000000
--- a/cmds/statsd/src/hash.cpp
+++ /dev/null
@@ -1,142 +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.
- */
-
-#include "hash.h"
-
-#ifndef FALLTHROUGH_INTENDED
-#define FALLTHROUGH_INTENDED [[fallthrough]]
-#endif
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-// Lower-level versions of Get... that read directly from a character buffer
-// without any bounds checking.
-inline uint32_t DecodeFixed32(const char *ptr) {
- return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0]))) |
- (static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8) |
- (static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16) |
- (static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24));
-}
-
-inline uint64_t DecodeFixed64(const char* ptr) {
- uint64_t lo = DecodeFixed32(ptr);
- uint64_t hi = DecodeFixed32(ptr + 4);
- return (hi << 32) | lo;
-}
-
-// 0xff is in case char is signed.
-static inline uint32_t ByteAs32(char c) { return static_cast<uint32_t>(c) & 0xff; }
-static inline uint64_t ByteAs64(char c) { return static_cast<uint64_t>(c) & 0xff; }
-
-} // namespace
-
-uint32_t Hash32(const char *data, size_t n, uint32_t seed) {
- // 'm' and 'r' are mixing constants generated offline.
- // They're not really 'magic', they just happen to work well.
- const uint32_t m = 0x5bd1e995;
- const int r = 24;
-
- // Initialize the hash to a 'random' value
- uint32_t h = static_cast<uint32_t>(seed ^ n);
-
- // Mix 4 bytes at a time into the hash
- while (n >= 4) {
- uint32_t k = DecodeFixed32(data);
- k *= m;
- k ^= k >> r;
- k *= m;
- h *= m;
- h ^= k;
- data += 4;
- n -= 4;
- }
-
- // Handle the last few bytes of the input array
- switch (n) {
- case 3:
- h ^= ByteAs32(data[2]) << 16;
- FALLTHROUGH_INTENDED;
- case 2:
- h ^= ByteAs32(data[1]) << 8;
- FALLTHROUGH_INTENDED;
- case 1:
- h ^= ByteAs32(data[0]);
- h *= m;
- }
-
- // Do a few final mixes of the hash to ensure the last few
- // bytes are well-incorporated.
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
- return h;
-}
-
-uint64_t Hash64(const char* data, size_t n, uint64_t seed) {
- const uint64_t m = 0xc6a4a7935bd1e995;
- const int r = 47;
-
- uint64_t h = seed ^ (n * m);
-
- while (n >= 8) {
- uint64_t k = DecodeFixed64(data);
- data += 8;
- n -= 8;
-
- k *= m;
- k ^= k >> r;
- k *= m;
-
- h ^= k;
- h *= m;
- }
-
- switch (n) {
- case 7:
- h ^= ByteAs64(data[6]) << 48;
- FALLTHROUGH_INTENDED;
- case 6:
- h ^= ByteAs64(data[5]) << 40;
- FALLTHROUGH_INTENDED;
- case 5:
- h ^= ByteAs64(data[4]) << 32;
- FALLTHROUGH_INTENDED;
- case 4:
- h ^= ByteAs64(data[3]) << 24;
- FALLTHROUGH_INTENDED;
- case 3:
- h ^= ByteAs64(data[2]) << 16;
- FALLTHROUGH_INTENDED;
- case 2:
- h ^= ByteAs64(data[1]) << 8;
- FALLTHROUGH_INTENDED;
- case 1:
- h ^= ByteAs64(data[0]);
- h *= m;
- }
-
- h ^= h >> r;
- h *= m;
- h ^= h >> r;
-
- return h;
-}
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/hash.h b/cmds/statsd/src/hash.h
deleted file mode 100644
index bd6b0cd47eaf..000000000000
--- a/cmds/statsd/src/hash.h
+++ /dev/null
@@ -1,47 +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.
- */
-
-#pragma once
-
-#include <string>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Uses murmur2 hashing algorithm.
-extern uint32_t Hash32(const char *data, size_t n, uint32_t seed);
-extern uint64_t Hash64(const char* data, size_t n, uint64_t seed);
-
-inline uint32_t Hash32(const char *data, size_t n) {
- return Hash32(data, n, 0xBEEF);
-}
-
-inline uint32_t Hash32(const std::string &input) {
- return Hash32(input.data(), input.size());
-}
-
-inline uint64_t Hash64(const char* data, size_t n) {
- return Hash64(data, n, 0xDECAFCAFFE);
-}
-
-inline uint64_t Hash64(const std::string& str) {
- return Hash64(str.data(), str.size());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
deleted file mode 100644
index f56fa6221bc9..000000000000
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ /dev/null
@@ -1,599 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "logd/LogEvent.h"
-
-#include <android-base/stringprintf.h>
-#include <android/binder_ibinder.h>
-#include <private/android_filesystem_config.h>
-
-#include "annotations.h"
-#include "stats_log_util.h"
-#include "statslog_statsd.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for TrainInfo experiment id serialization
-const int FIELD_ID_EXPERIMENT_ID = 1;
-
-using namespace android::util;
-using android::base::StringPrintf;
-using android::util::ProtoOutputStream;
-using std::string;
-using std::vector;
-
-// stats_event.h socket types. Keep in sync.
-/* ERRORS */
-#define ERROR_NO_TIMESTAMP 0x1
-#define ERROR_NO_ATOM_ID 0x2
-#define ERROR_OVERFLOW 0x4
-#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
-#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
-#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
-#define ERROR_INVALID_ANNOTATION_ID 0x40
-#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
-#define ERROR_TOO_MANY_ANNOTATIONS 0x100
-#define ERROR_TOO_MANY_FIELDS 0x200
-#define ERROR_INVALID_VALUE_TYPE 0x400
-#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
-
-/* TYPE IDS */
-#define INT32_TYPE 0x00
-#define INT64_TYPE 0x01
-#define STRING_TYPE 0x02
-#define LIST_TYPE 0x03
-#define FLOAT_TYPE 0x04
-#define BOOL_TYPE 0x05
-#define BYTE_ARRAY_TYPE 0x06
-#define OBJECT_TYPE 0x07
-#define KEY_VALUE_PAIRS_TYPE 0x08
-#define ATTRIBUTION_CHAIN_TYPE 0x09
-#define ERROR_TYPE 0x0F
-
-LogEvent::LogEvent(int32_t uid, int32_t pid)
- : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) {
-}
-
-LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
- bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
- const std::vector<uint8_t>& experimentIds, int32_t userId) {
- mLogdTimestampNs = getWallClockNs();
- mElapsedTimestampNs = getElapsedRealtimeNs();
- mTagId = util::BINARY_PUSH_STATE_CHANGED;
- mLogUid = AIBinder_getCallingUid();
- mLogPid = AIBinder_getCallingPid();
-
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled)));
- mValues.push_back(
- FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId)));
-}
-
-LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- const InstallTrainInfo& trainInfo) {
- mLogdTimestampNs = wallClockTimestampNs;
- mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = util::TRAIN_INFO;
-
- mValues.push_back(
- FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
- std::vector<uint8_t> experimentIdsProto;
- writeExperimentIdsToProto(trainInfo.experimentIds, &experimentIdsProto);
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(experimentIdsProto)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value(trainInfo.trainName)));
- mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
-}
-
-void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
- int32_t value = readNextValue<int32_t>();
- addToValues(pos, depth, value, last);
- parseAnnotations(numAnnotations);
-}
-
-void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
- int64_t value = readNextValue<int64_t>();
- addToValues(pos, depth, value, last);
- parseAnnotations(numAnnotations);
-}
-
-void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
- int32_t numBytes = readNextValue<int32_t>();
- if ((uint32_t)numBytes > mRemainingLen) {
- mValid = false;
- return;
- }
-
- string value = string((char*)mBuf, numBytes);
- mBuf += numBytes;
- mRemainingLen -= numBytes;
- addToValues(pos, depth, value, last);
- parseAnnotations(numAnnotations);
-}
-
-void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
- float value = readNextValue<float>();
- addToValues(pos, depth, value, last);
- parseAnnotations(numAnnotations);
-}
-
-void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
- // cast to int32_t because FieldValue does not support bools
- int32_t value = (int32_t)readNextValue<uint8_t>();
- addToValues(pos, depth, value, last);
- parseAnnotations(numAnnotations);
-}
-
-void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
- int32_t numBytes = readNextValue<int32_t>();
- if ((uint32_t)numBytes > mRemainingLen) {
- mValid = false;
- return;
- }
-
- vector<uint8_t> value(mBuf, mBuf + numBytes);
- mBuf += numBytes;
- mRemainingLen -= numBytes;
- addToValues(pos, depth, value, last);
- parseAnnotations(numAnnotations);
-}
-
-void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
- int32_t numPairs = readNextValue<uint8_t>();
-
- for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
- last[1] = (pos[1] == numPairs);
-
- // parse key
- pos[2] = 1;
- parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
-
- // parse value
- last[2] = true;
-
- uint8_t typeInfo = readNextValue<uint8_t>();
- switch (getTypeId(typeInfo)) {
- case INT32_TYPE:
- pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
- parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
- break;
- case INT64_TYPE:
- pos[2] = 3;
- parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
- break;
- case STRING_TYPE:
- pos[2] = 4;
- parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
- break;
- case FLOAT_TYPE:
- pos[2] = 5;
- parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
- break;
- default:
- mValid = false;
- }
- }
-
- parseAnnotations(numAnnotations);
-
- pos[1] = pos[2] = 1;
- last[1] = last[2] = false;
-}
-
-void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
- uint8_t numAnnotations) {
- const unsigned int firstUidInChainIndex = mValues.size();
- const int32_t numNodes = readNextValue<uint8_t>();
- for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
- last[1] = (pos[1] == numNodes);
-
- // parse uid
- pos[2] = 1;
- parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
-
- // parse tag
- pos[2] = 2;
- last[2] = true;
- parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
- }
- // Check if at least one node was successfully parsed.
- if (mValues.size() - 1 > firstUidInChainIndex) {
- mAttributionChainStartIndex = static_cast<int8_t>(firstUidInChainIndex);
- mAttributionChainEndIndex = static_cast<int8_t>(mValues.size() - 1);
- }
-
- parseAnnotations(numAnnotations, firstUidInChainIndex);
-
- pos[1] = pos[2] = 1;
- last[1] = last[2] = false;
-}
-
-// Assumes that mValues is not empty
-bool LogEvent::checkPreviousValueType(Type expected) {
- return mValues[mValues.size() - 1].mValue.getType() == expected;
-}
-
-void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
- if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
- mValid = false;
- return;
- }
-
- bool isUid = readNextValue<uint8_t>();
- if (isUid) mUidFieldIndex = static_cast<int8_t>(mValues.size() - 1);
- mValues[mValues.size() - 1].mAnnotations.setUidField(isUid);
-}
-
-void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
- if (!mValues.empty() || annotationType != BOOL_TYPE) {
- mValid = false;
- return;
- }
-
- mTruncateTimestamp = readNextValue<uint8_t>();
-}
-
-void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) {
- if (mValues.empty() || annotationType != BOOL_TYPE) {
- mValid = false;
- return;
- }
-
- const bool primaryField = readNextValue<uint8_t>();
- mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField);
-}
-
-void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,
- int firstUidInChainIndex) {
- if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) {
- mValid = false;
- return;
- }
-
- const bool primaryField = readNextValue<uint8_t>();
- mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField);
-}
-
-void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
- if (mValues.empty() || annotationType != BOOL_TYPE) {
- mValid = false;
- return;
- }
-
- const bool exclusiveState = readNextValue<uint8_t>();
- mExclusiveStateFieldIndex = static_cast<int8_t>(mValues.size() - 1);
- mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
-}
-
-void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
- if (mValues.empty() || annotationType != INT32_TYPE) {
- mValid = false;
- return;
- }
-
- mResetState = readNextValue<int32_t>();
-}
-
-void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
- if (mValues.empty() || annotationType != BOOL_TYPE) {
- mValid = false;
- return;
- }
-
- bool nested = readNextValue<uint8_t>();
- mValues[mValues.size() - 1].mAnnotations.setNested(nested);
-}
-
-// firstUidInChainIndex is a default parameter that is only needed when parsing
-// annotations for attribution chains.
-void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) {
- for (uint8_t i = 0; i < numAnnotations; i++) {
- uint8_t annotationId = readNextValue<uint8_t>();
- uint8_t annotationType = readNextValue<uint8_t>();
-
- switch (annotationId) {
- case ANNOTATION_ID_IS_UID:
- parseIsUidAnnotation(annotationType);
- break;
- case ANNOTATION_ID_TRUNCATE_TIMESTAMP:
- parseTruncateTimestampAnnotation(annotationType);
- break;
- case ANNOTATION_ID_PRIMARY_FIELD:
- parsePrimaryFieldAnnotation(annotationType);
- break;
- case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
- parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex);
- break;
- case ANNOTATION_ID_EXCLUSIVE_STATE:
- parseExclusiveStateAnnotation(annotationType);
- break;
- case ANNOTATION_ID_TRIGGER_STATE_RESET:
- parseTriggerStateResetAnnotation(annotationType);
- break;
- case ANNOTATION_ID_STATE_NESTED:
- parseStateNestedAnnotation(annotationType);
- break;
- default:
- mValid = false;
- return;
- }
- }
-}
-
-// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
-// stats_event.c
-bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
- mBuf = buf;
- mRemainingLen = (uint32_t)len;
-
- int32_t pos[] = {1, 1, 1};
- bool last[] = {false, false, false};
-
- // Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID
- uint8_t typeInfo = readNextValue<uint8_t>();
- if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false;
-
- uint8_t numElements = readNextValue<uint8_t>();
- if (numElements < 2 || numElements > 127) mValid = false;
-
- typeInfo = readNextValue<uint8_t>();
- if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
- mElapsedTimestampNs = readNextValue<int64_t>();
- numElements--;
-
- typeInfo = readNextValue<uint8_t>();
- if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
- mTagId = readNextValue<int32_t>();
- numElements--;
- parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
-
- for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
- last[0] = (pos[0] == numElements);
-
- typeInfo = readNextValue<uint8_t>();
- uint8_t typeId = getTypeId(typeInfo);
-
- switch (typeId) {
- case BOOL_TYPE:
- parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case INT32_TYPE:
- parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case INT64_TYPE:
- parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case FLOAT_TYPE:
- parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case BYTE_ARRAY_TYPE:
- parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case STRING_TYPE:
- parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case KEY_VALUE_PAIRS_TYPE:
- parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case ATTRIBUTION_CHAIN_TYPE:
- parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
- break;
- case ERROR_TYPE:
- /* mErrorBitmask =*/ readNextValue<int32_t>();
- mValid = false;
- break;
- default:
- mValid = false;
- break;
- }
- }
-
- if (mRemainingLen != 0) mValid = false;
- mBuf = nullptr;
- return mValid;
-}
-
-uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
- return typeInfo & 0x0F; // type id in lower 4 bytes
-}
-
-uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) {
- return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
-}
-
-int64_t LogEvent::GetLong(size_t key, status_t* err) const {
- // TODO(b/110561208): encapsulate the magical operations in Field struct as static functions
- int field = getSimpleField(key);
- for (const auto& value : mValues) {
- if (value.mField.getField() == field) {
- if (value.mValue.getType() == LONG) {
- return value.mValue.long_value;
- } else if (value.mValue.getType() == INT) {
- return value.mValue.int_value;
- } else {
- *err = BAD_TYPE;
- return 0;
- }
- }
- if ((size_t)value.mField.getPosAtDepth(0) > key) {
- break;
- }
- }
-
- *err = BAD_INDEX;
- return 0;
-}
-
-int LogEvent::GetInt(size_t key, status_t* err) const {
- int field = getSimpleField(key);
- for (const auto& value : mValues) {
- if (value.mField.getField() == field) {
- if (value.mValue.getType() == INT) {
- return value.mValue.int_value;
- } else {
- *err = BAD_TYPE;
- return 0;
- }
- }
- if ((size_t)value.mField.getPosAtDepth(0) > key) {
- break;
- }
- }
-
- *err = BAD_INDEX;
- return 0;
-}
-
-const char* LogEvent::GetString(size_t key, status_t* err) const {
- int field = getSimpleField(key);
- for (const auto& value : mValues) {
- if (value.mField.getField() == field) {
- if (value.mValue.getType() == STRING) {
- return value.mValue.str_value.c_str();
- } else {
- *err = BAD_TYPE;
- return 0;
- }
- }
- if ((size_t)value.mField.getPosAtDepth(0) > key) {
- break;
- }
- }
-
- *err = BAD_INDEX;
- return NULL;
-}
-
-bool LogEvent::GetBool(size_t key, status_t* err) const {
- int field = getSimpleField(key);
- for (const auto& value : mValues) {
- if (value.mField.getField() == field) {
- if (value.mValue.getType() == INT) {
- return value.mValue.int_value != 0;
- } else if (value.mValue.getType() == LONG) {
- return value.mValue.long_value != 0;
- } else {
- *err = BAD_TYPE;
- return false;
- }
- }
- if ((size_t)value.mField.getPosAtDepth(0) > key) {
- break;
- }
- }
-
- *err = BAD_INDEX;
- return false;
-}
-
-float LogEvent::GetFloat(size_t key, status_t* err) const {
- int field = getSimpleField(key);
- for (const auto& value : mValues) {
- if (value.mField.getField() == field) {
- if (value.mValue.getType() == FLOAT) {
- return value.mValue.float_value;
- } else {
- *err = BAD_TYPE;
- return 0.0;
- }
- }
- if ((size_t)value.mField.getPosAtDepth(0) > key) {
- break;
- }
- }
-
- *err = BAD_INDEX;
- return 0.0;
-}
-
-std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const {
- int field = getSimpleField(key);
- for (const auto& value : mValues) {
- if (value.mField.getField() == field) {
- if (value.mValue.getType() == STORAGE) {
- return value.mValue.storage_value;
- } else {
- *err = BAD_TYPE;
- return vector<uint8_t>();
- }
- }
- if ((size_t)value.mField.getPosAtDepth(0) > key) {
- break;
- }
- }
-
- *err = BAD_INDEX;
- return vector<uint8_t>();
-}
-
-string LogEvent::ToString() const {
- string result;
- result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
- (long long)mElapsedTimestampNs, mTagId);
- for (const auto& value : mValues) {
- result +=
- StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " ";
- }
- result += " }";
- return result;
-}
-
-void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
- writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
-}
-
-bool LogEvent::hasAttributionChain(std::pair<int, int>* indexRange) const {
- if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) {
- return false;
- }
-
- if (nullptr != indexRange) {
- indexRange->first = static_cast<int>(mAttributionChainStartIndex);
- indexRange->second = static_cast<int>(mAttributionChainEndIndex);
- }
-
- return true;
-}
-
-void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
- std::vector<uint8_t>* protoOut) {
- ProtoOutputStream proto;
- for (const auto& expId : experimentIds) {
- proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
- (long long)expId);
- }
-
- protoOut->resize(proto.size());
- size_t pos = 0;
- sp<ProtoReader> reader = proto.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(protoOut->data() + pos, reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
deleted file mode 100644
index a5f24603585a..000000000000
--- a/cmds/statsd/src/logd/LogEvent.h
+++ /dev/null
@@ -1,331 +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.
- */
-
-#pragma once
-
-#include "FieldValue.h"
-
-#include <android/util/ProtoOutputStream.h>
-#include <private/android_logger.h>
-
-#include <string>
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-struct InstallTrainInfo {
- int64_t trainVersionCode;
- std::string trainName;
- int32_t status;
- std::vector<int64_t> experimentIds;
- bool requiresStaging;
- bool rollbackEnabled;
- bool requiresLowLatencyMonitor;
-};
-
-/**
- * This class decodes the structured, serialized encoding of an atom into a
- * vector of FieldValues.
- */
-class LogEvent {
-public:
- /**
- * \param uid user id of the logging caller
- * \param pid process id of the logging caller
- */
- explicit LogEvent(int32_t uid, int32_t pid);
-
- /**
- * Parses the atomId, timestamp, and vector of values from a buffer
- * containing the StatsEvent/AStatsEvent encoding of an atom.
- *
- * \param buf a buffer that begins at the start of the serialized atom (it
- * should not include the android_log_header_t or the StatsEventTag)
- * \param len size of the buffer
- *
- * \return success of the initialization
- */
- bool parseBuffer(uint8_t* buf, size_t len);
-
- // Constructs a BinaryPushStateChanged LogEvent from API call.
- explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
- bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
- const std::vector<uint8_t>& experimentIds, int32_t userId);
-
- explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- const InstallTrainInfo& installTrainInfo);
-
- ~LogEvent() {}
-
- /**
- * Get the timestamp associated with this event.
- */
- inline int64_t GetLogdTimestampNs() const { return mLogdTimestampNs; }
- inline int64_t GetElapsedTimestampNs() const { return mElapsedTimestampNs; }
-
- /**
- * Get the tag for this event.
- */
- inline int GetTagId() const { return mTagId; }
-
- /**
- * Get the uid of the logging client.
- * Returns -1 if the uid is unknown/has not been set.
- */
- inline int32_t GetUid() const { return mLogUid; }
-
- /**
- * Get the pid of the logging client.
- * Returns -1 if the pid is unknown/has not been set.
- */
- inline int32_t GetPid() const { return mLogPid; }
-
- /**
- * Get the nth value, starting at 1.
- *
- * Returns BAD_INDEX if the index is larger than the number of elements.
- * Returns BAD_TYPE if the index is available but the data is the wrong type.
- */
- int64_t GetLong(size_t key, status_t* err) const;
- int GetInt(size_t key, status_t* err) const;
- const char* GetString(size_t key, status_t* err) const;
- bool GetBool(size_t key, status_t* err) const;
- float GetFloat(size_t key, status_t* err) const;
- std::vector<uint8_t> GetStorage(size_t key, status_t* err) const;
-
- /**
- * Return a string representation of this event.
- */
- std::string ToString() const;
-
- /**
- * Write this object to a ProtoOutputStream.
- */
- void ToProto(android::util::ProtoOutputStream& out) const;
-
- /**
- * Set elapsed timestamp if the original timestamp is missing.
- */
- void setElapsedTimestampNs(int64_t timestampNs) {
- mElapsedTimestampNs = timestampNs;
- }
-
- /**
- * Set the timestamp if the original logd timestamp is missing.
- */
- void setLogdWallClockTimestampNs(int64_t timestampNs) {
- mLogdTimestampNs = timestampNs;
- }
-
- inline int size() const {
- return mValues.size();
- }
-
- const std::vector<FieldValue>& getValues() const {
- return mValues;
- }
-
- std::vector<FieldValue>* getMutableValues() {
- return &mValues;
- }
-
- // Default value = false
- inline bool shouldTruncateTimestamp() const {
- return mTruncateTimestamp;
- }
-
- // Returns the index of the uid field within the FieldValues vector if the
- // uid exists. If there is no uid field, returns -1.
- //
- // If the index within the atom definition is desired, do the following:
- // int vectorIndex = LogEvent.getUidFieldIndex();
- // if (vectorIndex != -1) {
- // FieldValue& v = LogEvent.getValues()[vectorIndex];
- // int atomIndex = v.mField.getPosAtDepth(0);
- // }
- // Note that atomIndex is 1-indexed.
- inline int getUidFieldIndex() {
- return static_cast<int>(mUidFieldIndex);
- }
-
- // Returns whether this LogEvent has an AttributionChain.
- // If it does and indexRange is not a nullptr, populate indexRange with the start and end index
- // of the AttributionChain within mValues.
- bool hasAttributionChain(std::pair<int, int>* indexRange = nullptr) const;
-
- // Returns the index of the exclusive state field within the FieldValues vector if
- // an exclusive state exists. If there is no exclusive state field, returns -1.
- //
- // If the index within the atom definition is desired, do the following:
- // int vectorIndex = LogEvent.getExclusiveStateFieldIndex();
- // if (vectorIndex != -1) {
- // FieldValue& v = LogEvent.getValues()[vectorIndex];
- // int atomIndex = v.mField.getPosAtDepth(0);
- // }
- // Note that atomIndex is 1-indexed.
- inline int getExclusiveStateFieldIndex() const {
- return static_cast<int>(mExclusiveStateFieldIndex);
- }
-
- // If a reset state is not sent in the StatsEvent, returns -1. Note that a
- // reset state is sent if and only if a reset should be triggered.
- inline int getResetState() const {
- return mResetState;
- }
-
- inline LogEvent makeCopy() {
- return LogEvent(*this);
- }
-
- template <class T>
- status_t updateValue(size_t key, T& value, Type type) {
- int field = getSimpleField(key);
- for (auto& fieldValue : mValues) {
- if (fieldValue.mField.getField() == field) {
- if (fieldValue.mValue.getType() == type) {
- fieldValue.mValue = Value(value);
- return OK;
- } else {
- return BAD_TYPE;
- }
- }
- }
- return BAD_INDEX;
- }
-
- bool isValid() const {
- return mValid;
- }
-
-private:
- /**
- * Only use this if copy is absolutely needed.
- */
- LogEvent(const LogEvent&) = default;
-
- void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
- void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
- void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
- void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
- void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
- void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
- void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
- void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
-
- void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1);
- void parseIsUidAnnotation(uint8_t annotationType);
- void parseTruncateTimestampAnnotation(uint8_t annotationType);
- void parsePrimaryFieldAnnotation(uint8_t annotationType);
- void parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex);
- void parseExclusiveStateAnnotation(uint8_t annotationType);
- void parseTriggerStateResetAnnotation(uint8_t annotationType);
- void parseStateNestedAnnotation(uint8_t annotationType);
- bool checkPreviousValueType(Type expected);
-
- /**
- * The below two variables are only valid during the execution of
- * parseBuffer. There are no guarantees about the state of these variables
- * before/after.
- */
- uint8_t* mBuf;
- uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
-
- bool mValid = true; // stores whether the event we received from the socket is valid
-
- /**
- * Side-effects:
- * If there is enough space in buffer to read value of type T
- * - move mBuf past the value that was just read
- * - decrement mRemainingLen by size of T
- * Else
- * - set mValid to false
- */
- template <class T>
- T readNextValue() {
- T value;
- if (mRemainingLen < sizeof(T)) {
- mValid = false;
- value = 0; // all primitive types can successfully cast 0
- } else {
- // When alignof(T) == 1, hopefully the compiler can optimize away
- // this conditional as always true.
- if ((reinterpret_cast<uintptr_t>(mBuf) % alignof(T)) == 0) {
- // We're properly aligned, and can safely make this assignment.
- value = *((T*)mBuf);
- } else {
- // We need to use memcpy. It's slower, but safe.
- memcpy(&value, mBuf, sizeof(T));
- }
- mBuf += sizeof(T);
- mRemainingLen -= sizeof(T);
- }
- return value;
- }
-
- template <class T>
- void addToValues(int32_t* pos, int32_t depth, T& value, bool* last) {
- Field f = Field(mTagId, pos, depth);
- // do not decorate last position at depth 0
- for (int i = 1; i < depth; i++) {
- if (last[i]) f.decorateLastPos(i);
- }
-
- Value v = Value(value);
- mValues.push_back(FieldValue(f, v));
- }
-
- uint8_t getTypeId(uint8_t typeInfo);
- uint8_t getNumAnnotations(uint8_t typeInfo);
-
- // The items are naturally sorted in DFS order as we read them. this allows us to do fast
- // matching.
- std::vector<FieldValue> mValues;
-
- // The timestamp set by the logd.
- int64_t mLogdTimestampNs;
-
- // The elapsed timestamp set by statsd log writer.
- int64_t mElapsedTimestampNs;
-
- // The atom tag of the event (defaults to 0 if client does not
- // appropriately set the atom id).
- int mTagId = 0;
-
- // The uid of the logging client (defaults to -1).
- int32_t mLogUid = -1;
-
- // The pid of the logging client (defaults to -1).
- int32_t mLogPid = -1;
-
- // Annotations
- bool mTruncateTimestamp = false;
- int mResetState = -1;
-
- // Indexes within the FieldValue vector can be stored in 7 bits because
- // that's the assumption enforced by the encoding used in FieldValue.
- int8_t mUidFieldIndex = -1;
- int8_t mAttributionChainStartIndex = -1;
- int8_t mAttributionChainEndIndex = -1;
- int8_t mExclusiveStateFieldIndex = -1;
-};
-
-void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/logd/LogEventQueue.cpp b/cmds/statsd/src/logd/LogEventQueue.cpp
deleted file mode 100644
index 146464bbe774..000000000000
--- a/cmds/statsd/src/logd/LogEventQueue.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "LogEventQueue.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unique_lock;
-using std::unique_ptr;
-
-unique_ptr<LogEvent> LogEventQueue::waitPop() {
- std::unique_lock<std::mutex> lock(mMutex);
-
- if (mQueue.empty()) {
- mCondition.wait(lock, [this] { return !this->mQueue.empty(); });
- }
-
- unique_ptr<LogEvent> item = std::move(mQueue.front());
- mQueue.pop();
-
- return item;
-}
-
-bool LogEventQueue::push(unique_ptr<LogEvent> item, int64_t* oldestTimestampNs) {
- bool success;
- {
- std::unique_lock<std::mutex> lock(mMutex);
- if (mQueue.size() < mQueueLimit) {
- mQueue.push(std::move(item));
- success = true;
- } else {
- // safe operation as queue must not be empty.
- *oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs();
- success = false;
- }
- }
-
- mCondition.notify_one();
- return success;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/logd/LogEventQueue.h b/cmds/statsd/src/logd/LogEventQueue.h
deleted file mode 100644
index 9dda3d24c571..000000000000
--- a/cmds/statsd/src/logd/LogEventQueue.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include "LogEvent.h"
-
-#include <condition_variable>
-#include <mutex>
-#include <queue>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * A zero copy thread safe queue buffer for producing and consuming LogEvent.
- */
-class LogEventQueue {
-public:
- explicit LogEventQueue(size_t maxSize) : mQueueLimit(maxSize){};
-
- /**
- * Blocking read one event from the queue.
- */
- std::unique_ptr<LogEvent> waitPop();
-
- /**
- * Puts a LogEvent ptr to the end of the queue.
- * Returns false on failure when the queue is full, and output the oldest event timestamp
- * in the queue.
- */
- bool push(std::unique_ptr<LogEvent> event, int64_t* oldestTimestampNs);
-
-private:
- const size_t mQueueLimit;
- std::condition_variable mCondition;
- std::mutex mMutex;
- std::queue<std::unique_ptr<LogEvent>> mQueue;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
deleted file mode 100644
index 03b178a989eb..000000000000
--- a/cmds/statsd/src/main.cpp
+++ /dev/null
@@ -1,113 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StatsService.h"
-#include "socket/StatsSocketListener.h"
-
-#include <android/binder_interface_utils.h>
-#include <android/binder_process.h>
-#include <android/binder_manager.h>
-#include <utils/Looper.h>
-
-#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-using namespace android;
-using namespace android::os::statsd;
-using ::ndk::SharedRefBase;
-using std::shared_ptr;
-using std::make_shared;
-
-shared_ptr<StatsService> gStatsService = nullptr;
-sp<StatsSocketListener> gSocketListener = nullptr;
-
-void signalHandler(int sig) {
- if (sig == SIGPIPE) {
- // ShellSubscriber uses SIGPIPE as a signal to detect the end of the
- // client process. Don't prematurely exit(1) here. Instead, ignore the
- // signal and allow the write call to return EPIPE.
- ALOGI("statsd received SIGPIPE. Ignoring signal.");
- return;
- }
-
- if (gSocketListener != nullptr) gSocketListener->stopListener();
- if (gStatsService != nullptr) gStatsService->Terminate();
- ALOGW("statsd terminated on receiving signal %d.", sig);
- exit(1);
-}
-
-void registerSignalHandlers()
-{
- struct sigaction sa;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- sa.sa_handler = signalHandler;
- sigaction(SIGPIPE, &sa, nullptr);
- sigaction(SIGHUP, &sa, nullptr);
- sigaction(SIGINT, &sa, nullptr);
- sigaction(SIGQUIT, &sa, nullptr);
- sigaction(SIGTERM, &sa, nullptr);
-}
-
-int main(int /*argc*/, char** /*argv*/) {
- // Set up the looper
- sp<Looper> looper(Looper::prepare(0 /* opts */));
-
- // Set up the binder
- ABinderProcess_setThreadPoolMaxThreadCount(9);
- ABinderProcess_startThreadPool();
-
- std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(4000 /*buffer limit. Buffer is NOT pre-allocated*/);
-
- // Create the service
- gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
- // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports
- // setting dumpsys priorities.
- binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats");
- if (status != STATUS_OK) {
- ALOGE("Failed to add service as AIDL service");
- return -1;
- }
-
- registerSignalHandlers();
-
- gStatsService->sayHiToStatsCompanion();
-
- gStatsService->Startup();
-
- gSocketListener = new StatsSocketListener(eventQueue);
-
- ALOGI("Statsd starts to listen to socket.");
- // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
- if (gSocketListener->startListener(600)) {
- exit(1);
- }
-
- // Loop forever -- the reports run on this thread in a handler, and the
- // binder calls remain responsive in their pool of one thread.
- while (true) {
- looper->pollAll(-1 /* timeoutMillis */);
- }
- ALOGW("statsd escaped from its loop.");
-
- return 1;
-}
diff --git a/cmds/statsd/src/matchers/AtomMatchingTracker.h b/cmds/statsd/src/matchers/AtomMatchingTracker.h
deleted file mode 100644
index c1384972464c..000000000000
--- a/cmds/statsd/src/matchers/AtomMatchingTracker.h
+++ /dev/null
@@ -1,118 +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.
- */
-
-#ifndef ATOM_MATCHING_TRACKER_H
-#define ATOM_MATCHING_TRACKER_H
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "logd/LogEvent.h"
-#include "matchers/matcher_util.h"
-
-#include <utils/RefBase.h>
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class AtomMatchingTracker : public virtual RefBase {
-public:
- AtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash)
- : mId(id), mIndex(index), mInitialized(false), mProtoHash(protoHash){};
-
- virtual ~AtomMatchingTracker(){};
-
- // Initialize this AtomMatchingTracker.
- // allAtomMatchers: the list of the AtomMatcher proto config. This is needed because we don't
- // store the proto object in memory. We only need it during initilization.
- // allAtomMatchingTrackers: the list of the AtomMatchingTracker objects. It's a one-to-one
- // mapping with allAtomMatchers. This is needed because the
- // initialization is done recursively for
- // CombinationAtomMatchingTrackers using DFS.
- // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
- // circle dependency.
- virtual bool init(const std::vector<AtomMatcher>& allAtomMatchers,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& matcherMap,
- std::vector<bool>& stack) = 0;
-
- // Update appropriate state on config updates. Primarily, all indices need to be updated.
- // This matcher and all of its children are guaranteed to be preserved across the update.
- // matcher: the AtomMatcher proto from the config.
- // index: the index of this matcher in mAllAtomMatchingTrackers.
- // atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers
- virtual bool onConfigUpdated(
- const AtomMatcher& matcher, const int index,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0;
-
- // Called when a log event comes.
- // event: the log event.
- // allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log
- // processing is done recursively.
- // matcherResults: The cached results for all matchers for this event. Parent matchers can
- // directly access the children's matching results if they have been evaluated.
- // Otherwise, call children matchers' onLogEvent.
- virtual void onLogEvent(const LogEvent& event,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::vector<MatchingState>& matcherResults) = 0;
-
- // Get the tagIds that this matcher cares about. The combined collection is stored
- // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
- // some memory but hopefully it can save us much CPU time when there is flood of events.
- virtual const std::set<int>& getAtomIds() const {
- return mAtomIds;
- }
-
- int64_t getId() const {
- return mId;
- }
-
- uint64_t getProtoHash() const {
- return mProtoHash;
- }
-
-protected:
- // Name of this matching. We don't really need the name, but it makes log message easy to debug.
- const int64_t mId;
-
- // Index of this AtomMatchingTracker in MetricsManager's container.
- int mIndex;
-
- // Whether this AtomMatchingTracker has been properly initialized.
- bool mInitialized;
-
- // The collection of the event tag ids that this AtomMatchingTracker cares. So we can quickly
- // return kNotMatched when we receive an event with an id not in the list. This is especially
- // useful when we have a complex CombinationAtomMatchingTracker.
- std::set<int> mAtomIds;
-
- // Hash of the AtomMatcher's proto bytes from StatsdConfig.
- // Used to determine if the definition of this matcher has changed across a config update.
- const uint64_t mProtoHash;
-
- FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple);
- FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // ATOM_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
deleted file mode 100644
index 45685ce5bfee..000000000000
--- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
+++ /dev/null
@@ -1,140 +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.
- */
-
-#include "Log.h"
-
-#include "CombinationAtomMatchingTracker.h"
-
-#include "matchers/matcher_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t& id, const int index,
- const uint64_t protoHash)
- : AtomMatchingTracker(id, index, protoHash) {
-}
-
-CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() {
-}
-
-bool CombinationAtomMatchingTracker::init(
- const vector<AtomMatcher>& allAtomMatchers,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) {
- if (mInitialized) {
- return true;
- }
-
- // mark this node as visited in the recursion stack.
- stack[mIndex] = true;
-
- AtomMatcher_Combination matcher = allAtomMatchers[mIndex].combination();
-
- // LogicalOperation is missing in the config
- if (!matcher.has_operation()) {
- return false;
- }
-
- mLogicalOperation = matcher.operation();
-
- if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
- return false;
- }
-
- for (const auto& child : matcher.matcher()) {
- auto pair = matcherMap.find(child);
- if (pair == matcherMap.end()) {
- ALOGW("Matcher %lld not found in the config", (long long)child);
- return false;
- }
-
- int childIndex = pair->second;
-
- // if the child is a visited node in the recursion -> circle detected.
- if (stack[childIndex]) {
- ALOGE("Circle detected in matcher config");
- return false;
- }
-
- if (!allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers,
- matcherMap, stack)) {
- ALOGW("child matcher init failed %lld", (long long)child);
- return false;
- }
-
- mChildren.push_back(childIndex);
-
- const set<int>& childTagIds = allAtomMatchingTrackers[childIndex]->getAtomIds();
- mAtomIds.insert(childTagIds.begin(), childTagIds.end());
- }
-
- mInitialized = true;
- // unmark this node in the recursion stack.
- stack[mIndex] = false;
- return true;
-}
-
-bool CombinationAtomMatchingTracker::onConfigUpdated(
- const AtomMatcher& matcher, const int index,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
- mIndex = index;
- mChildren.clear();
- AtomMatcher_Combination combinationMatcher = matcher.combination();
- for (const int64_t child : combinationMatcher.matcher()) {
- const auto& pair = atomMatchingTrackerMap.find(child);
- if (pair == atomMatchingTrackerMap.end()) {
- ALOGW("Matcher %lld not found in the config", (long long)child);
- return false;
- }
- mChildren.push_back(pair->second);
- }
- return true;
-}
-
-void CombinationAtomMatchingTracker::onLogEvent(
- const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- vector<MatchingState>& matcherResults) {
- // this event has been processed.
- if (matcherResults[mIndex] != MatchingState::kNotComputed) {
- return;
- }
-
- if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
- matcherResults[mIndex] = MatchingState::kNotMatched;
- return;
- }
-
- // evaluate children matchers if they haven't been evaluated.
- for (const int childIndex : mChildren) {
- if (matcherResults[childIndex] == MatchingState::kNotComputed) {
- const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex];
- child->onLogEvent(event, allAtomMatchingTrackers, matcherResults);
- }
- }
-
- bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
- matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
deleted file mode 100644
index 3160448b6c76..000000000000
--- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
+++ /dev/null
@@ -1,58 +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.
- */
-#ifndef COMBINATION_ATOM_MATCHING_TRACKER_H
-#define COMBINATION_ATOM_MATCHING_TRACKER_H
-
-#include <unordered_map>
-#include <vector>
-
-#include "AtomMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Represents a AtomMatcher_Combination in the StatsdConfig.
-class CombinationAtomMatchingTracker : public AtomMatchingTracker {
-public:
- CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash);
-
- bool init(const std::vector<AtomMatcher>& allAtomMatchers,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack);
-
- bool onConfigUpdated(const AtomMatcher& matcher, const int index,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
-
- ~CombinationAtomMatchingTracker();
-
- void onLogEvent(const LogEvent& event,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::vector<MatchingState>& matcherResults) override;
-
-private:
- LogicalOperation mLogicalOperation;
-
- std::vector<int> mChildren;
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // COMBINATION_ATOM_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.cpp b/cmds/statsd/src/matchers/EventMatcherWizard.cpp
deleted file mode 100644
index 025c9a87b16b..000000000000
--- a/cmds/statsd/src/matchers/EventMatcherWizard.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "EventMatcherWizard.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) {
- if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) {
- return MatchingState::kNotComputed;
- }
- vector<MatchingState> matcherCache(mAllEventMatchers.size(), MatchingState::kNotComputed);
- mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, matcherCache);
- return matcherCache[matcher_index];
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.h b/cmds/statsd/src/matchers/EventMatcherWizard.h
deleted file mode 100644
index 5d780f2423d8..000000000000
--- a/cmds/statsd/src/matchers/EventMatcherWizard.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "AtomMatchingTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class EventMatcherWizard : public virtual android::RefBase {
-public:
- EventMatcherWizard(){}; // for testing
- EventMatcherWizard(const std::vector<sp<AtomMatchingTracker>>& eventTrackers)
- : mAllEventMatchers(eventTrackers){};
-
- virtual ~EventMatcherWizard(){};
-
- MatchingState matchLogEvent(const LogEvent& event, int matcher_index);
-
-private:
- std::vector<sp<AtomMatchingTracker>> mAllEventMatchers;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
deleted file mode 100644
index 423da5bd3cf8..000000000000
--- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
+++ /dev/null
@@ -1,81 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "SimpleAtomMatchingTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-using std::vector;
-
-SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t& id, const int index,
- const uint64_t protoHash,
- const SimpleAtomMatcher& matcher,
- const sp<UidMap>& uidMap)
- : AtomMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) {
- if (!matcher.has_atom_id()) {
- mInitialized = false;
- } else {
- mAtomIds.insert(matcher.atom_id());
- mInitialized = true;
- }
-}
-
-SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() {
-}
-
-bool SimpleAtomMatchingTracker::init(const vector<AtomMatcher>& allAtomMatchers,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& matcherMap,
- vector<bool>& stack) {
- // no need to do anything.
- return mInitialized;
-}
-
-bool SimpleAtomMatchingTracker::onConfigUpdated(
- const AtomMatcher& matcher, const int index,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
- mIndex = index;
- // Do not need to update mMatcher since the matcher must be identical across the update.
- return mInitialized;
-}
-
-void SimpleAtomMatchingTracker::onLogEvent(
- const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- vector<MatchingState>& matcherResults) {
- if (matcherResults[mIndex] != MatchingState::kNotComputed) {
- VLOG("Matcher %lld already evaluated ", (long long)mId);
- return;
- }
-
- if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
- matcherResults[mIndex] = MatchingState::kNotMatched;
- return;
- }
-
- bool matched = matchesSimple(mUidMap, mMatcher, event);
- matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
- VLOG("Stats SimpleAtomMatcher %lld matched? %d", (long long)mId, matched);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
deleted file mode 100644
index b67e6c20e8f1..000000000000
--- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
+++ /dev/null
@@ -1,58 +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.
- */
-
-#ifndef SIMPLE_ATOM_MATCHING_TRACKER_H
-#define SIMPLE_ATOM_MATCHING_TRACKER_H
-
-#include <unordered_map>
-#include <vector>
-
-#include "AtomMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "packages/UidMap.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class SimpleAtomMatchingTracker : public AtomMatchingTracker {
-public:
- SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash,
- const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap);
-
- ~SimpleAtomMatchingTracker();
-
- bool init(const std::vector<AtomMatcher>& allAtomMatchers,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& matcherMap,
- std::vector<bool>& stack) override;
-
- bool onConfigUpdated(const AtomMatcher& matcher, const int index,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
-
- void onLogEvent(const LogEvent& event,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::vector<MatchingState>& matcherResults) override;
-
-private:
- const SimpleAtomMatcher mMatcher;
- const sp<UidMap> mUidMap;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // SIMPLE_ATOM_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
deleted file mode 100644
index a7454c5d923b..000000000000
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ /dev/null
@@ -1,373 +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.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "matchers/AtomMatchingTracker.h"
-#include "matchers/matcher_util.h"
-#include "stats_util.h"
-
-using std::set;
-using std::string;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-bool combinationMatch(const vector<int>& children, const LogicalOperation& operation,
- const vector<MatchingState>& matcherResults) {
- bool matched;
- switch (operation) {
- case LogicalOperation::AND: {
- matched = true;
- for (const int childIndex : children) {
- if (matcherResults[childIndex] != MatchingState::kMatched) {
- matched = false;
- break;
- }
- }
- break;
- }
- case LogicalOperation::OR: {
- matched = false;
- for (const int childIndex : children) {
- if (matcherResults[childIndex] == MatchingState::kMatched) {
- matched = true;
- break;
- }
- }
- break;
- }
- case LogicalOperation::NOT:
- matched = matcherResults[children[0]] == MatchingState::kNotMatched;
- break;
- case LogicalOperation::NAND:
- matched = false;
- for (const int childIndex : children) {
- if (matcherResults[childIndex] != MatchingState::kMatched) {
- matched = true;
- break;
- }
- }
- break;
- case LogicalOperation::NOR:
- matched = true;
- for (const int childIndex : children) {
- if (matcherResults[childIndex] == MatchingState::kMatched) {
- matched = false;
- break;
- }
- }
- break;
- case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
- matched = false;
- break;
- }
- return matched;
-}
-
-bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
- const string& str_match) {
- if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
- int uid = fieldValue.mValue.int_value;
- auto aidIt = UidMap::sAidToUidMapping.find(str_match);
- if (aidIt != UidMap::sAidToUidMapping.end()) {
- return ((int)aidIt->second) == uid;
- }
- std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
- return packageNames.find(str_match) != packageNames.end();
- } else if (fieldValue.mValue.getType() == STRING) {
- return fieldValue.mValue.str_value == str_match;
- }
- return false;
-}
-
-bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
- const vector<FieldValue>& values, int start, int end, int depth) {
- if (depth > 2) {
- ALOGE("Depth > 3 not supported");
- return false;
- }
-
- if (start >= end) {
- return false;
- }
-
- // Filter by entry field first
- int newStart = -1;
- int newEnd = end;
- // because the fields are naturally sorted in the DFS order. we can safely
- // break when pos is larger than the one we are searching for.
- for (int i = start; i < end; i++) {
- int pos = values[i].mField.getPosAtDepth(depth);
- if (pos == matcher.field()) {
- if (newStart == -1) {
- newStart = i;
- }
- newEnd = i + 1;
- } else if (pos > matcher.field()) {
- break;
- }
- }
-
- // Now we have zoomed in to a new range
- start = newStart;
- end = newEnd;
-
- if (start == -1) {
- // No such field found.
- return false;
- }
-
- vector<pair<int, int>> ranges; // the ranges are for matching ANY position
- if (matcher.has_position()) {
- // Repeated fields position is stored as a node in the path.
- depth++;
- if (depth > 2) {
- return false;
- }
- switch (matcher.position()) {
- case Position::FIRST: {
- for (int i = start; i < end; i++) {
- int pos = values[i].mField.getPosAtDepth(depth);
- if (pos != 1) {
- // Again, the log elements are stored in sorted order. so
- // once the position is > 1, we break;
- end = i;
- break;
- }
- }
- ranges.push_back(std::make_pair(start, end));
- break;
- }
- case Position::LAST: {
- // move the starting index to the first LAST field at the depth.
- for (int i = start; i < end; i++) {
- if (values[i].mField.isLastPos(depth)) {
- start = i;
- break;
- }
- }
- ranges.push_back(std::make_pair(start, end));
- break;
- }
- case Position::ANY: {
- // ANY means all the children matchers match in any of the sub trees, it's a match
- newStart = start;
- newEnd = end;
- // Here start is guaranteed to be a valid index.
- int currentPos = values[start].mField.getPosAtDepth(depth);
- // Now find all sub trees ranges.
- for (int i = start; i < end; i++) {
- int newPos = values[i].mField.getPosAtDepth(depth);
- if (newPos != currentPos) {
- ranges.push_back(std::make_pair(newStart, i));
- newStart = i;
- currentPos = newPos;
- }
- }
- ranges.push_back(std::make_pair(newStart, end));
- break;
- }
- case Position::ALL:
- ALOGE("Not supported: field matcher with ALL position.");
- break;
- case Position::POSITION_UNKNOWN:
- break;
- }
- } else {
- // No position
- ranges.push_back(std::make_pair(start, end));
- }
- // start and end are still pointing to the matched range.
- switch (matcher.value_matcher_case()) {
- case FieldValueMatcher::kMatchesTuple: {
- ++depth;
- // If any range matches all matchers, good.
- for (const auto& range : ranges) {
- bool matched = true;
- for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) {
- if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second,
- depth)) {
- matched = false;
- break;
- }
- }
- if (matched) return true;
- }
- return false;
- }
- // Finally, we get to the point of real value matching.
- // If the field matcher ends with ANY, then we have [start, end) range > 1.
- // In the following, we should return true, when ANY of the values matches.
- case FieldValueMatcher::ValueMatcherCase::kEqBool: {
- for (int i = start; i < end; i++) {
- if ((values[i].mValue.getType() == INT &&
- (values[i].mValue.int_value != 0) == matcher.eq_bool()) ||
- (values[i].mValue.getType() == LONG &&
- (values[i].mValue.long_value != 0) == matcher.eq_bool())) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kEqString: {
- for (int i = start; i < end; i++) {
- if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: {
- const auto& str_list = matcher.neq_any_string();
- for (int i = start; i < end; i++) {
- bool notEqAll = true;
- for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i], str)) {
- notEqAll = false;
- break;
- }
- }
- if (notEqAll) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kEqAnyString: {
- const auto& str_list = matcher.eq_any_string();
- for (int i = start; i < end; i++) {
- for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i], str)) {
- return true;
- }
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kEqInt: {
- for (int i = start; i < end; i++) {
- if (values[i].mValue.getType() == INT &&
- (matcher.eq_int() == values[i].mValue.int_value)) {
- return true;
- }
- // eq_int covers both int and long.
- if (values[i].mValue.getType() == LONG &&
- (matcher.eq_int() == values[i].mValue.long_value)) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kLtInt: {
- for (int i = start; i < end; i++) {
- if (values[i].mValue.getType() == INT &&
- (values[i].mValue.int_value < matcher.lt_int())) {
- return true;
- }
- // lt_int covers both int and long.
- if (values[i].mValue.getType() == LONG &&
- (values[i].mValue.long_value < matcher.lt_int())) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kGtInt: {
- for (int i = start; i < end; i++) {
- if (values[i].mValue.getType() == INT &&
- (values[i].mValue.int_value > matcher.gt_int())) {
- return true;
- }
- // gt_int covers both int and long.
- if (values[i].mValue.getType() == LONG &&
- (values[i].mValue.long_value > matcher.gt_int())) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
- for (int i = start; i < end; i++) {
- if (values[i].mValue.getType() == FLOAT &&
- (values[i].mValue.float_value < matcher.lt_float())) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
- for (int i = start; i < end; i++) {
- if (values[i].mValue.getType() == FLOAT &&
- (values[i].mValue.float_value > matcher.gt_float())) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kLteInt: {
- for (int i = start; i < end; i++) {
- if (values[i].mValue.getType() == INT &&
- (values[i].mValue.int_value <= matcher.lte_int())) {
- return true;
- }
- // lte_int covers both int and long.
- if (values[i].mValue.getType() == LONG &&
- (values[i].mValue.long_value <= matcher.lte_int())) {
- return true;
- }
- }
- return false;
- }
- case FieldValueMatcher::ValueMatcherCase::kGteInt: {
- for (int i = start; i < end; i++) {
- if (values[i].mValue.getType() == INT &&
- (values[i].mValue.int_value >= matcher.gte_int())) {
- return true;
- }
- // gte_int covers both int and long.
- if (values[i].mValue.getType() == LONG &&
- (values[i].mValue.long_value >= matcher.gte_int())) {
- return true;
- }
- }
- return false;
- }
- default:
- return false;
- }
-}
-
-bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
- const LogEvent& event) {
- if (event.GetTagId() != simpleMatcher.atom_id()) {
- return false;
- }
-
- for (const auto& matcher : simpleMatcher.field_value_matcher()) {
- if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
- return false;
- }
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
deleted file mode 100644
index 130b6068bd19..000000000000
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ /dev/null
@@ -1,44 +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.
- */
-
-#pragma once
-
-#include "logd/LogEvent.h"
-
-#include <vector>
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "packages/UidMap.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-enum MatchingState {
- kNotComputed = -1,
- kNotMatched = 0,
- kMatched = 1,
-};
-
-bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
- const std::vector<MatchingState>& matcherResults);
-
-bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
- const LogEvent& wrapper);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metadata_util.cpp b/cmds/statsd/src/metadata_util.cpp
deleted file mode 100644
index 27ee59b36242..000000000000
--- a/cmds/statsd/src/metadata_util.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 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 "FieldValue.h"
-#include "metadata_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using google::protobuf::RepeatedPtrField;
-
-void writeValueToProto(metadata::FieldValue* metadataFieldValue, const Value& value) {
- std::string storage_value;
- switch (value.getType()) {
- case INT:
- metadataFieldValue->set_value_int(value.int_value);
- break;
- case LONG:
- metadataFieldValue->set_value_long(value.long_value);
- break;
- case FLOAT:
- metadataFieldValue->set_value_float(value.float_value);
- break;
- case DOUBLE:
- metadataFieldValue->set_value_double(value.double_value);
- break;
- case STRING:
- metadataFieldValue->set_value_str(value.str_value.c_str());
- break;
- case STORAGE: // byte array
- storage_value = ((char*) value.storage_value.data());
- metadataFieldValue->set_value_storage(storage_value);
- break;
- default:
- break;
- }
-}
-
-void writeMetricDimensionKeyToMetadataDimensionKey(
- const MetricDimensionKey& metricKey,
- metadata::MetricDimensionKey* metadataMetricKey) {
- for (const FieldValue& fieldValue : metricKey.getDimensionKeyInWhat().getValues()) {
- metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_dimension_key_in_what();
- metadata::Field* metadataField = metadataFieldValue->mutable_field();
- metadataField->set_tag(fieldValue.mField.getTag());
- metadataField->set_field(fieldValue.mField.getField());
- writeValueToProto(metadataFieldValue, fieldValue.mValue);
- }
-
- for (const FieldValue& fieldValue : metricKey.getStateValuesKey().getValues()) {
- metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_state_values_key();
- metadata::Field* metadataField = metadataFieldValue->mutable_field();
- metadataField->set_tag(fieldValue.mField.getTag());
- metadataField->set_field(fieldValue.mField.getField());
- writeValueToProto(metadataFieldValue, fieldValue.mValue);
- }
-}
-
-void writeFieldValuesFromMetadata(
- const RepeatedPtrField<metadata::FieldValue>& repeatedFieldValueList,
- std::vector<FieldValue>* fieldValues) {
- for (const metadata::FieldValue& metadataFieldValue : repeatedFieldValueList) {
- Field field(metadataFieldValue.field().tag(), metadataFieldValue.field().field());
- Value value;
- switch (metadataFieldValue.value_case()) {
- case metadata::FieldValue::ValueCase::kValueInt:
- value = Value(metadataFieldValue.value_int());
- break;
- case metadata::FieldValue::ValueCase::kValueLong:
- value = Value(metadataFieldValue.value_long());
- break;
- case metadata::FieldValue::ValueCase::kValueFloat:
- value = Value(metadataFieldValue.value_float());
- break;
- case metadata::FieldValue::ValueCase::kValueDouble:
- value = Value(metadataFieldValue.value_double());
- break;
- case metadata::FieldValue::ValueCase::kValueStr:
- value = Value(metadataFieldValue.value_str());
- break;
- case metadata::FieldValue::ValueCase::kValueStorage:
- value = Value(metadataFieldValue.value_storage());
- break;
- default:
- break;
- }
- FieldValue fieldValue(field, value);
- fieldValues->emplace_back(field, value);
- }
-}
-
-MetricDimensionKey loadMetricDimensionKeyFromProto(
- const metadata::MetricDimensionKey& metricDimensionKey) {
- std::vector<FieldValue> dimKeyInWhatFieldValues;
- writeFieldValuesFromMetadata(metricDimensionKey.dimension_key_in_what(),
- &dimKeyInWhatFieldValues);
- std::vector<FieldValue> stateValuesFieldValues;
- writeFieldValuesFromMetadata(metricDimensionKey.state_values_key(), &stateValuesFieldValues);
-
- HashableDimensionKey dimKeyInWhat(dimKeyInWhatFieldValues);
- HashableDimensionKey stateValues(stateValuesFieldValues);
- MetricDimensionKey metricKey(dimKeyInWhat, stateValues);
- return metricKey;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/metadata_util.h b/cmds/statsd/src/metadata_util.h
deleted file mode 100644
index 84a39ff872b5..000000000000
--- a/cmds/statsd/src/metadata_util.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2020 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 "HashableDimensionKey.h"
-
-#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
-
-namespace android {
-namespace os {
-namespace statsd {
-
-void writeMetricDimensionKeyToMetadataDimensionKey(const MetricDimensionKey& metricKey,
- metadata::MetricDimensionKey* metadataMetricKey);
-
-MetricDimensionKey loadMetricDimensionKeyFromProto(
- const metadata::MetricDimensionKey& metricDimensionKey);
-
-} // namespace statsd
-} // namespace os
-} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
deleted file mode 100644
index a8ef54a335c4..000000000000
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ /dev/null
@@ -1,440 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "CountMetricProducer.h"
-
-#include <inttypes.h>
-#include <limits.h>
-#include <stdlib.h>
-
-#include "guardrail/StatsdStats.h"
-#include "metrics/parsing_utils/metrics_manager_util.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;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-using std::map;
-using std::string;
-using std::unordered_map;
-using std::vector;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for StatsLogReport
-const int FIELD_ID_ID = 1;
-const int FIELD_ID_COUNT_METRICS = 5;
-const int FIELD_ID_TIME_BASE = 9;
-const int FIELD_ID_BUCKET_SIZE = 10;
-const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_IS_ACTIVE = 14;
-
-// for CountMetricDataWrapper
-const int FIELD_ID_DATA = 1;
-// for CountMetricData
-const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_SLICE_BY_STATE = 6;
-const int FIELD_ID_BUCKET_INFO = 3;
-const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-// for CountBucketInfo
-const int FIELD_ID_COUNT = 3;
-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 vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const uint64_t protoHash, 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, initialConditionCache, wizard,
- protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap) {
- if (metric.has_bucket()) {
- mBucketSizeNs =
- TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
- } else {
- mBucketSizeNs = LLONG_MAX;
- }
-
- if (metric.has_dimensions_in_what()) {
- translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
- mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
- }
-
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
-
- if (metric.links().size() > 0) {
- for (const auto& link : metric.links()) {
- Metric2Condition mc;
- mc.conditionId = link.condition();
- translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
- translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
- mMetric2ConditionLinks.push_back(mc);
- }
- mConditionSliced = true;
- }
-
- for (const auto& stateLink : metric.state_link()) {
- Metric2State ms;
- ms.stateAtomId = stateLink.state_atom_id();
- translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
- translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
- mMetric2StateLinks.push_back(ms);
- }
-
- flushIfNeededLocked(startTimeNs);
- // Adjust start for partial bucket
- mCurrentBucketStartTimeNs = startTimeNs;
-
- VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
- (long long)mBucketSizeNs, (long long)mTimeBaseNs);
-}
-
-CountMetricProducer::~CountMetricProducer() {
- VLOG("~CountMetricProducer() called");
-}
-
-bool CountMetricProducer::onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!MetricProducer::onConfigUpdatedLocked(
- config, configIndex, metricIndex, allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
- trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- return false;
- }
-
- const CountMetric& metric = config.count_metric(configIndex);
- int trackerIndex;
- // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return false;
- }
-
- if (metric.has_condition() &&
- !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, mConditionTrackerIndex,
- conditionToMetricMap)) {
- return false;
- }
- return true;
-}
-
-void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey,
- const FieldValue& oldState, const FieldValue& newState) {
- VLOG("CountMetric %lld onStateChanged time %lld, State%d, key %s, %d -> %d",
- (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
- oldState.mValue.int_value, newState.mValue.int_value);
-}
-
-void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
- if (mCurrentSlicedCounter == nullptr ||
- mCurrentSlicedCounter->size() == 0) {
- return;
- }
-
- fprintf(out, "CountMetric %lld dimension size %lu\n", (long long)mMetricId,
- (unsigned long)mCurrentSlicedCounter->size());
- if (verbose) {
- for (const auto& it : *mCurrentSlicedCounter) {
- fprintf(out, "\t(what)%s\t(state)%s %lld\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getStateValuesKey().toString().c_str(), (unsigned long long)it.second);
- }
- }
-}
-
-void CountMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
-}
-
-
-void CountMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
- mPastBuckets.clear();
-}
-
-void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- if (include_current_partial_bucket) {
- flushLocked(dumpTimeNs);
- } else {
- flushIfNeededLocked(dumpTimeNs);
- }
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
-
-
- if (mPastBuckets.empty()) {
- return;
- }
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
-
- // Fills the dimension path if not slicing by ALL.
- if (!mSliceByPositionALL) {
- if (!mDimensionsInWhat.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
- writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
- protoOutput->end(dimenPathToken);
- }
- }
-
- uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
-
- for (const auto& counter : mPastBuckets) {
- const MetricDimensionKey& dimensionKey = counter.first;
- VLOG(" dimension key %s", dimensionKey.toString().c_str());
-
- uint64_t wrapperToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
-
- // First fill dimension.
- if (mSliceByPositionALL) {
- uint64_t dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
- protoOutput->end(dimensionToken);
- } else {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
- FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- }
- // Then fill slice_by_state.
- for (auto state : dimensionKey.getStateValuesKey().getValues()) {
- uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SLICE_BY_STATE);
- writeStateToProto(state, protoOutput);
- protoOutput->end(stateToken);
- }
- // Then fill bucket_info (CountBucketInfo).
- for (const auto& bucket : counter.second) {
- uint64_t bucketInfoToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
- // Partial bucket.
- if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketStartNs));
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketEndNs));
- } else {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
- (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
- }
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
- protoOutput->end(bucketInfoToken);
- VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, (long long)bucket.mCount);
- }
- protoOutput->end(wrapperToken);
- }
-
- protoOutput->end(protoToken);
-
- if (erase_data) {
- mPastBuckets.clear();
- }
-}
-
-void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
- flushIfNeededLocked(dropTimeNs);
- StatsdStats::getInstance().noteBucketDropped(mMetricId);
- mPastBuckets.clear();
-}
-
-void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
- const int64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
- mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
-}
-
-bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
- if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
- return false;
- }
- // ===========GuardRail==============
- // 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("CountMetric %lld dropping data for dimension key %s",
- (long long)mMetricId, newKey.toString().c_str());
- StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
- return true;
- }
- }
-
- return false;
-}
-
-void CountMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const map<int, HashableDimensionKey>& statePrimaryKeys) {
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
- flushIfNeededLocked(eventTimeNs);
-
- if (!condition) {
- return;
- }
-
- auto it = mCurrentSlicedCounter->find(eventKey);
- if (it == mCurrentSlicedCounter->end()) {
- // ===========GuardRail==============
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
- // create a counter for the new key
- (*mCurrentSlicedCounter)[eventKey] = 1;
- } else {
- // increment the existing value
- auto& count = it->second;
- count++;
- }
- for (auto& tracker : mAnomalyTrackers) {
- int64_t countWholeBucket = mCurrentSlicedCounter->find(eventKey)->second;
- auto prev = mCurrentFullCounters->find(eventKey);
- if (prev != mCurrentFullCounters->end()) {
- countWholeBucket += prev->second;
- }
- tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
- countWholeBucket);
- }
-
- VLOG("metric %lld %s->%lld", (long long)mMetricId, eventKey.toString().c_str(),
- (long long)(*mCurrentSlicedCounter)[eventKey]);
-}
-
-// When a new matched event comes in, we check if event falls into the current
-// bucket. If not, flush the old counter to past buckets and initialize the new bucket.
-void CountMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
- int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
- if (eventTimeNs < currentBucketEndTimeNs) {
- return;
- }
-
- // Setup the bucket start time and number.
- int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
- int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
- flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
-
- mCurrentBucketNum += numBucketsForward;
- VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
- (long long)mCurrentBucketStartTimeNs);
-}
-
-void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {
- int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
- CountBucket info;
- info.mBucketStartNs = mCurrentBucketStartTimeNs;
- if (eventTimeNs < fullBucketEndTimeNs) {
- info.mBucketEndNs = eventTimeNs;
- } else {
- info.mBucketEndNs = fullBucketEndTimeNs;
- }
- for (const auto& counter : *mCurrentSlicedCounter) {
- info.mCount = counter.second;
- auto& bucketList = mPastBuckets[counter.first];
- bucketList.push_back(info);
- VLOG("metric %lld, dump key value: %s -> %lld", (long long)mMetricId,
- counter.first.toString().c_str(),
- (long long)counter.second);
- }
-
- // If we have finished a full bucket, then send this to anomaly tracker.
- if (eventTimeNs > fullBucketEndTimeNs) {
- // Accumulate partial buckets with current value and then send to anomaly tracker.
- if (mCurrentFullCounters->size() > 0) {
- for (const auto& keyValuePair : *mCurrentSlicedCounter) {
- (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second;
- }
- for (auto& tracker : mAnomalyTrackers) {
- tracker->addPastBucket(mCurrentFullCounters, mCurrentBucketNum);
- }
- mCurrentFullCounters = std::make_shared<DimToValMap>();
- } else {
- // Skip aggregating the partial buckets since there's no previous partial bucket.
- for (auto& tracker : mAnomalyTrackers) {
- tracker->addPastBucket(mCurrentSlicedCounter, mCurrentBucketNum);
- }
- }
- } else {
- // Accumulate partial bucket.
- for (const auto& keyValuePair : *mCurrentSlicedCounter) {
- (*mCurrentFullCounters)[keyValuePair.first] += keyValuePair.second;
- }
- }
-
- StatsdStats::getInstance().noteBucketCount(mMetricId);
- // Only resets the counters, but doesn't setup the times nor numbers.
- // (Do not clear since the old one is still referenced in mAnomalyTrackers).
- mCurrentSlicedCounter = std::make_shared<DimToValMap>();
- mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
-}
-
-// Rough estimate of CountMetricProducer buffer stored. This number will be
-// greater than actual data size as it contains each dimension of
-// CountMetricData is duplicated.
-size_t CountMetricProducer::byteSizeLocked() const {
- size_t totalSize = 0;
- for (const auto& pair : mPastBuckets) {
- totalSize += pair.second.size() * kBucketSize;
- }
- return totalSize;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
deleted file mode 100644
index 0769ac459b8c..000000000000
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ /dev/null
@@ -1,143 +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.
- */
-
-#ifndef COUNT_METRIC_PRODUCER_H
-#define COUNT_METRIC_PRODUCER_H
-
-#include <android/util/ProtoOutputStream.h>
-#include <gtest/gtest_prod.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 {
-namespace os {
-namespace statsd {
-
-struct CountBucket {
- int64_t mBucketStartNs;
- int64_t mBucketEndNs;
- int64_t mCount;
-};
-
-class CountMetricProducer : public MetricProducer {
-public:
- CountMetricProducer(
- const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const uint64_t protoHash, 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(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const FieldValue& oldState,
- const FieldValue& newState) override;
-
- MetricType getMetricType() const override {
- return METRIC_TYPE_COUNT;
- }
-
-protected:
- void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
-
-private:
-
- void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) override;
-
- void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
-
- // Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
-
- // Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
-
- // Internal function to calculate the current used bytes.
- size_t byteSizeLocked() const override;
-
- void dumpStatesLocked(FILE* out, bool verbose) const override;
-
- void dropDataLocked(const int64_t dropTimeNs) override;
-
- // Util function to flush the old packet.
- void flushIfNeededLocked(const int64_t& newEventTime) override;
-
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
-
- bool onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation) override;
-
- std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
-
- // The current bucket (may be a partial bucket).
- std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
-
- // The sum of previous partial buckets in the current full bucket (excluding the current
- // partial bucket). This is only updated while flushing the current bucket.
- std::shared_ptr<DimToValMap> mCurrentFullCounters = std::make_shared<DimToValMap>();
-
- static const size_t kBucketSize = sizeof(CountBucket{});
-
- bool hitGuardRailLocked(const MetricDimensionKey& newKey);
-
- FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
- FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
- FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
- FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
- FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
- FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
-
- FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket);
- FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // COUNT_METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
deleted file mode 100644
index 8869241ab8aa..000000000000
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ /dev/null
@@ -1,747 +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.
- */
-
-#define DEBUG false
-
-#include "Log.h"
-
-#include "DurationMetricProducer.h"
-
-#include <limits.h>
-#include <stdlib.h>
-
-#include "guardrail/StatsdStats.h"
-#include "metrics/parsing_utils/metrics_manager_util.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;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-using std::string;
-using std::unordered_map;
-using std::vector;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for StatsLogReport
-const int FIELD_ID_ID = 1;
-const int FIELD_ID_DURATION_METRICS = 6;
-const int FIELD_ID_TIME_BASE = 9;
-const int FIELD_ID_BUCKET_SIZE = 10;
-const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_IS_ACTIVE = 14;
-// for DurationMetricDataWrapper
-const int FIELD_ID_DATA = 1;
-// for DurationMetricData
-const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_BUCKET_INFO = 3;
-const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_SLICE_BY_STATE = 6;
-// for DurationBucketInfo
-const int FIELD_ID_DURATION = 3;
-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;
-
-DurationMetricProducer::DurationMetricProducer(
- const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const int startIndex,
- const int stopIndex, const int stopAllIndex, const bool nesting,
- const sp<ConditionWizard>& wizard, const uint64_t protoHash,
- 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)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
- protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap),
- mAggregationType(metric.aggregation_type()),
- mStartIndex(startIndex),
- mStopIndex(stopIndex),
- mStopAllIndex(stopAllIndex),
- mNested(nesting),
- mContainANYPositionInInternalDimensions(false) {
- if (metric.has_bucket()) {
- mBucketSizeNs =
- TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
- } else {
- mBucketSizeNs = LLONG_MAX;
- }
-
- if (metric.has_dimensions_in_what()) {
- translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
- mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
- }
-
- if (internalDimensions.has_field()) {
- translateFieldMatcher(internalDimensions, &mInternalDimensions);
- mContainANYPositionInInternalDimensions = HasPositionANY(internalDimensions);
- }
- if (mContainANYPositionInInternalDimensions) {
- ALOGE("Position ANY in internal dimension not supported.");
- }
- if (mContainANYPositionInDimensionsInWhat) {
- ALOGE("Position ANY in dimension_in_what not supported.");
- }
-
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
-
- if (metric.links().size() > 0) {
- for (const auto& link : metric.links()) {
- Metric2Condition mc;
- mc.conditionId = link.condition();
- translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
- translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
- mMetric2ConditionLinks.push_back(mc);
- }
- mConditionSliced = true;
- }
- mUnSlicedPartCondition = ConditionState::kUnknown;
-
- for (const auto& stateLink : metric.state_link()) {
- Metric2State ms;
- ms.stateAtomId = stateLink.state_atom_id();
- translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
- translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
- mMetric2StateLinks.push_back(ms);
- }
-
- mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
- if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
- mMetric2ConditionLinks.size() == 1) {
- mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions(
- mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields);
- }
- flushIfNeededLocked(startTimeNs);
- // Adjust start for partial bucket
- mCurrentBucketStartTimeNs = startTimeNs;
- VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
- (long long)mBucketSizeNs, (long long)mTimeBaseNs);
-}
-
-DurationMetricProducer::~DurationMetricProducer() {
- VLOG("~DurationMetric() called");
-}
-
-bool DurationMetricProducer::onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!MetricProducer::onConfigUpdatedLocked(
- config, configIndex, metricIndex, allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
- trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- return false;
- }
-
- const DurationMetric& metric = config.duration_metric(configIndex);
- const auto& what_it = conditionTrackerMap.find(metric.what());
- if (what_it == conditionTrackerMap.end()) {
- ALOGE("DurationMetric's \"what\" is not present in the config");
- return false;
- }
-
- const Predicate& durationWhat = config.predicate(what_it->second);
- if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
- ALOGE("DurationMetric's \"what\" must be a simple condition");
- return false;
- }
-
- const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
-
- // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager
- // maps.
- if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, newAtomMatchingTrackerMap,
- trackerToMetricMap, mStartIndex)) {
- ALOGE("Duration metrics must specify a valid start event matcher");
- return false;
- }
-
- if (simplePredicate.has_stop() &&
- !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, newAtomMatchingTrackerMap,
- trackerToMetricMap, mStopIndex)) {
- return false;
- }
-
- if (simplePredicate.has_stop_all() &&
- !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, newAtomMatchingTrackerMap,
- trackerToMetricMap, mStopAllIndex)) {
- return false;
- }
-
- if (metric.has_condition() &&
- !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, mConditionTrackerIndex,
- conditionToMetricMap)) {
- return false;
- }
-
- for (const auto& it : mCurrentSlicedDurationTrackerMap) {
- it.second->onConfigUpdated(wizard, mConditionTrackerIndex);
- }
-
- return true;
-}
-
-sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
- const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mAggregationType == DurationMetric_AggregationType_SUM) {
- if (alert.trigger_if_sum_gt() > alert.num_buckets() * mBucketSizeNs) {
- ALOGW("invalid alert for SUM: threshold (%f) > possible recordable value (%d x %lld)",
- alert.trigger_if_sum_gt(), alert.num_buckets(), (long long)mBucketSizeNs);
- return nullptr;
- }
- }
- sp<AnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
- addAnomalyTrackerLocked(anomalyTracker);
- return anomalyTracker;
-}
-
-// Adds an AnomalyTracker that has already been created.
-// Note: this gets called on config updates, and will only get called if the metric and the
-// associated alert are preserved, which means the AnomalyTracker must be a DurationAnomalyTracker.
-void DurationMetricProducer::addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
- std::lock_guard<std::mutex> lock(mMutex);
- addAnomalyTrackerLocked(anomalyTracker);
-}
-
-void DurationMetricProducer::addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker) {
- mAnomalyTrackers.push_back(anomalyTracker);
- for (const auto& [_, durationTracker] : mCurrentSlicedDurationTrackerMap) {
- durationTracker->addAnomalyTracker(anomalyTracker);
- }
-}
-void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey,
- const FieldValue& oldState,
- const FieldValue& newState) {
- // Check if this metric has a StateMap. If so, map the new state value to
- // the correct state group id.
- FieldValue newStateCopy = newState;
- mapStateValue(atomId, &newStateCopy);
-
- flushIfNeededLocked(eventTimeNs);
-
- // Each duration tracker is mapped to a different whatKey (a set of values from the
- // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
- // state change event are a subset of the tracker's whatKey field values.
- //
- // Ex. For a duration metric dimensioned on uid and tag:
- // DurationTracker1 whatKey = uid: 1001, tag: 1
- // DurationTracker2 whatKey = uid: 1002, tag 1
- //
- // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
- // change.
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
- continue;
- }
- whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
- }
-}
-
-unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
- const MetricDimensionKey& eventKey) const {
- switch (mAggregationType) {
- case DurationMetric_AggregationType_SUM:
- return make_unique<OringDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
- mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
- mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
- case DurationMetric_AggregationType_MAX_SPARSE:
- return make_unique<MaxDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
- mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
- mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
- }
-}
-
-// SlicedConditionChange optimization case 1:
-// 1. If combination condition, logical operation is AND, only one sliced child predicate.
-// 2. The links covers all dimension fields in the sliced child condition predicate.
-void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
- const int64_t eventTime) {
- if (mMetric2ConditionLinks.size() != 1 ||
- !mHasLinksToAllConditionDimensionsInTracker) {
- return;
- }
-
- bool currentUnSlicedPartCondition = true;
- if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
- ConditionState unslicedPartState =
- mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
- // When the unsliced part is still false, return directly.
- if (mUnSlicedPartCondition == ConditionState::kFalse &&
- unslicedPartState == ConditionState::kFalse) {
- return;
- }
- mUnSlicedPartCondition = unslicedPartState;
- currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
- }
-
- auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
- auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
-
- // The condition change is from the unsliced predicates.
- // We need to find out the true dimensions from the sliced predicate and flip their condition
- // state based on the new unsliced condition state.
- if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
- (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
- std::set<HashableDimensionKey> trueConditionDimensions;
- mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- HashableDimensionKey linkedConditionDimensionKey;
- getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
- &linkedConditionDimensionKey);
- if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
- trueConditionDimensions.end()) {
- whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
- }
- }
- } else {
- // Handle the condition change from the sliced predicate.
- if (currentUnSlicedPartCondition) {
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- HashableDimensionKey linkedConditionDimensionKey;
- getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
- &linkedConditionDimensionKey);
- if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
- dimensionsChangedToTrue->end()) {
- whatIt.second->onConditionChanged(true, eventTime);
- }
- if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
- dimensionsChangedToFalse->end()) {
- whatIt.second->onConditionChanged(false, eventTime);
- }
- }
- }
- }
-}
-
-void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition,
- const int64_t eventTimeNs) {
- bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
- if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
- onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs);
- return;
- }
-
- // Now for each of the on-going event, check if the condition has changed for them.
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- }
-}
-
-void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
-
- if (!mIsActive) {
- return;
- }
-
- flushIfNeededLocked(eventTime);
-
- if (!mConditionSliced) {
- return;
- }
-
- onSlicedConditionMayChangeInternalLocked(overallCondition, eventTime);
-}
-
-void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
- MetricProducer::onActiveStateChangedLocked(eventTimeNs);
-
- if (!mConditionSliced) {
- if (ConditionState::kTrue != mCondition) {
- return;
- }
-
- if (mIsActive) {
- flushIfNeededLocked(eventTimeNs);
- }
-
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
- }
- } else if (mIsActive) {
- flushIfNeededLocked(eventTimeNs);
- onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
- } else { // mConditionSliced == true && !mIsActive
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
- }
- }
-}
-
-void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
- const int64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
- mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
-
- if (!mIsActive) {
- return;
- }
-
- flushIfNeededLocked(eventTime);
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->onConditionChanged(conditionMet, eventTime);
- }
-}
-
-void DurationMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
- flushIfNeededLocked(dropTimeNs);
- StatsdStats::getInstance().noteBucketDropped(mMetricId);
- mPastBuckets.clear();
-}
-
-void DurationMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
- flushIfNeededLocked(dumpTimeNs);
- mPastBuckets.clear();
-}
-
-void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- if (include_current_partial_bucket) {
- flushLocked(dumpTimeNs);
- } else {
- flushIfNeededLocked(dumpTimeNs);
- }
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
-
- if (mPastBuckets.empty()) {
- VLOG(" Duration metric, empty return");
- return;
- }
-
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
-
- if (!mSliceByPositionALL) {
- if (!mDimensionsInWhat.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
- writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
- protoOutput->end(dimenPathToken);
- }
- }
-
- uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
-
- VLOG("Duration metric %lld dump report now...", (long long)mMetricId);
-
- for (const auto& pair : mPastBuckets) {
- const MetricDimensionKey& dimensionKey = pair.first;
- VLOG(" dimension key %s", dimensionKey.toString().c_str());
-
- uint64_t wrapperToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
-
- // First fill dimension.
- if (mSliceByPositionALL) {
- uint64_t dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
- protoOutput->end(dimensionToken);
- } else {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
- FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- }
- // Then fill slice_by_state.
- for (auto state : dimensionKey.getStateValuesKey().getValues()) {
- uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SLICE_BY_STATE);
- writeStateToProto(state, protoOutput);
- protoOutput->end(stateToken);
- }
- // Then fill bucket_info (DurationBucketInfo).
- for (const auto& bucket : pair.second) {
- uint64_t bucketInfoToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
- if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketStartNs));
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketEndNs));
- } else {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
- (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
- }
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
- protoOutput->end(bucketInfoToken);
- VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
- }
-
- protoOutput->end(wrapperToken);
- }
-
- protoOutput->end(protoToken);
- if (erase_data) {
- mPastBuckets.clear();
- }
-}
-
-void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
- int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
-
- if (currentBucketEndTimeNs > eventTimeNs) {
- return;
- }
- VLOG("flushing...........");
- int numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
- int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
- flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
-
- mCurrentBucketNum += numBucketsForward;
-}
-
-void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {
- for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
- whatIt != mCurrentSlicedDurationTrackerMap.end();) {
- if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
- VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
- whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
- } else {
- ++whatIt;
- }
- }
- StatsdStats::getInstance().noteBucketCount(mMetricId);
- mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
-}
-
-void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
- if (mCurrentSlicedDurationTrackerMap.size() == 0) {
- return;
- }
-
- fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
- (unsigned long)mCurrentSlicedDurationTrackerMap.size());
- if (verbose) {
- for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
- whatIt.second->dumpStates(out, verbose);
- }
- }
-}
-
-bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
- if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
- // 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(
- mConfigKey, mMetricId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("DurationMetric %lld dropping data for what dimension key %s",
- (long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
- StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
- return true;
- }
- }
- }
- return false;
-}
-
-void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys,
- bool condition, const LogEvent& event) {
- const auto& whatKey = eventKey.getDimensionKeyInWhat();
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
- if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
- mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
- }
-
- auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
- if (mUseWhatDimensionAsInternalDimension) {
- it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
- return;
- }
-
- if (mInternalDimensions.empty()) {
- it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
- conditionKeys);
- } else {
- HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
- filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
- it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
- conditionKeys);
- }
-
-}
-
-void DurationMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
- const map<int, HashableDimensionKey>& statePrimaryKeys) {
- ALOGW("Not used in duration tracker.");
-}
-
-void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
- const LogEvent& event) {
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
- if (eventTimeNs < mTimeBaseNs) {
- return;
- }
-
- if (mIsActive) {
- flushIfNeededLocked(event.GetElapsedTimestampNs());
- }
-
- // Handles Stopall events.
- if ((int)matcherIndex == mStopAllIndex) {
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
- }
- return;
- }
-
- HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
- if (!mDimensionsInWhat.empty()) {
- filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- }
-
- // Stores atom id to primary key pairs for each state atom that the metric is
- // sliced by.
- std::map<int, HashableDimensionKey> statePrimaryKeys;
-
- // For states with primary fields, use MetricStateLinks to get the primary
- // field values from the log event. These values will form a primary key
- // that will be used to query StateTracker for the correct state value.
- for (const auto& stateLink : mMetric2StateLinks) {
- getDimensionForState(event.getValues(), stateLink,
- &statePrimaryKeys[stateLink.stateAtomId]);
- }
-
- // For each sliced state, query StateTracker for the state value using
- // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
- //
- // Expected functionality: for any case where the MetricStateLinks are
- // initialized incorrectly (ex. # of state links != # of primary fields, no
- // links are provided for a state with primary fields, links are provided
- // in the wrong order, etc.), StateTracker will simply return kStateUnknown
- // when queried using an incorrect key.
- HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
- for (auto atomId : mSlicedStateAtoms) {
- FieldValue value;
- if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
- // found a primary key for this state, query using the key
- queryStateValue(atomId, statePrimaryKeys[atomId], &value);
- } else {
- // if no MetricStateLinks exist for this state atom,
- // query using the default dimension key (empty HashableDimensionKey)
- queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
- }
- mapStateValue(atomId, &value);
- stateValuesKey.addValue(value);
- }
-
- // Handles Stop events.
- if ((int)matcherIndex == mStopIndex) {
- if (mUseWhatDimensionAsInternalDimension) {
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
- }
- return;
- }
-
- HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
- if (!mInternalDimensions.empty()) {
- filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
- }
-
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
- }
- return;
- }
-
- bool condition;
- ConditionKey conditionKey;
- if (mConditionSliced) {
- for (const auto& link : mMetric2ConditionLinks) {
- getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
- }
-
- auto conditionState =
- mWizard->query(mConditionTrackerIndex, conditionKey,
- !mHasLinksToAllConditionDimensionsInTracker);
- condition = conditionState == ConditionState::kTrue;
- } else {
- // TODO: The unknown condition state is not handled here, we should fix it.
- condition = mCondition == ConditionState::kTrue;
- }
-
- condition = condition && mIsActive;
-
- handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
- event);
-}
-
-size_t DurationMetricProducer::byteSizeLocked() const {
- size_t totalSize = 0;
- for (const auto& pair : mPastBuckets) {
- totalSize += pair.second.size() * kBucketSize;
- }
- return totalSize;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
deleted file mode 100644
index 5feb09fc1c98..000000000000
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ /dev/null
@@ -1,194 +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.
- */
-
-#pragma once
-
-
-#include <unordered_map>
-
-#include <android/util/ProtoOutputStream.h>
-#include "../anomaly/DurationAnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
-#include "MetricProducer.h"
-#include "duration_helper/DurationTracker.h"
-#include "duration_helper/MaxDurationTracker.h"
-#include "duration_helper/OringDurationTracker.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
-
-using namespace std;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class DurationMetricProducer : public MetricProducer {
-public:
- DurationMetricProducer(
- const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const int startIndex,
- const int stopIndex, const int stopAllIndex, const bool nesting,
- const sp<ConditionWizard>& wizard, const uint64_t protoHash,
- 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();
-
- sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
- const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
-
- void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) override;
-
- void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const FieldValue& oldState,
- const FieldValue& newState) override;
-
- MetricType getMetricType() const override {
- return METRIC_TYPE_DURATION;
- }
-
-protected:
- void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
-
- void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
- const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
-
-private:
- void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
- bool condition, const LogEvent& event);
-
- void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) override;
-
- void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
-
- // Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
-
- // Internal interface to handle active state change.
- void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
-
- // Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
-
- void onSlicedConditionMayChangeInternalLocked(bool overallCondition,
- const int64_t eventTimeNs);
-
- void onSlicedConditionMayChangeLocked_opt1(bool overallCondition, const int64_t eventTime);
- void onSlicedConditionMayChangeLocked_opt2(bool overallCondition, const int64_t eventTime);
-
- // Internal function to calculate the current used bytes.
- size_t byteSizeLocked() const override;
-
- void dumpStatesLocked(FILE* out, bool verbose) const override;
-
- void dropDataLocked(const int64_t dropTimeNs) override;
-
- // Util function to flush the old packet.
- void flushIfNeededLocked(const int64_t& eventTime);
-
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
-
- bool onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation) override;
-
- void addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker);
-
- const DurationMetric_AggregationType mAggregationType;
-
- // Index of the SimpleAtomMatcher which defines the start.
- int mStartIndex;
-
- // Index of the SimpleAtomMatcher which defines the stop.
- int mStopIndex;
-
- // Index of the SimpleAtomMatcher which defines the stop all for all dimensions.
- int mStopAllIndex;
-
- // nest counting -- for the same key, stops must match the number of starts to make real stop
- const bool mNested;
-
- // The dimension from the atom predicate. e.g., uid, wakelock name.
- vector<Matcher> mInternalDimensions;
-
- bool mContainANYPositionInInternalDimensions;
-
- // This boolean is true iff When mInternalDimensions == mDimensionsInWhat
- bool mUseWhatDimensionAsInternalDimension;
-
- // Caches the current unsliced part condition.
- ConditionState mUnSlicedPartCondition;
-
- // Save the past buckets and we can clear when the StatsLogReport is dumped.
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
-
- // The duration trackers in the current bucket.
- std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
- mCurrentSlicedDurationTrackerMap;
-
- // Helper function to create a duration tracker given the metric aggregation type.
- std::unique_ptr<DurationTracker> createDurationTracker(
- const MetricDimensionKey& eventKey) const;
-
- // Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const MetricDimensionKey& newKey);
-
- static const size_t kBucketSize = sizeof(DurationBucket{});
-
- FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
- FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
- FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
- FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
-
- FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration);
- FRIEND_TEST(DurationMetricProducerTest_PartialBucket,
- TestSumDurationWithSplitInFollowingBucket);
- FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
- FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
deleted file mode 100644
index ca302c0e71fb..000000000000
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ /dev/null
@@ -1,214 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "EventMetricProducer.h"
-
-#include <limits.h>
-#include <stdlib.h>
-
-#include "metrics/parsing_utils/metrics_manager_util.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;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_STRING;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::ProtoOutputStream;
-using std::map;
-using std::string;
-using std::unordered_map;
-using std::vector;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for StatsLogReport
-const int FIELD_ID_ID = 1;
-const int FIELD_ID_EVENT_METRICS = 4;
-const int FIELD_ID_IS_ACTIVE = 14;
-// for EventMetricDataWrapper
-const int FIELD_ID_DATA = 1;
-// for EventMetricData
-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 vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const uint64_t protoHash, 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, initialConditionCache, wizard,
- protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
- stateGroupMap) {
- if (metric.links().size() > 0) {
- for (const auto& link : metric.links()) {
- Metric2Condition mc;
- mc.conditionId = link.condition();
- translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
- translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
- mMetric2ConditionLinks.push_back(mc);
- }
- mConditionSliced = true;
- }
- mProto = std::make_unique<ProtoOutputStream>();
- VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
- (long long)mBucketSizeNs, (long long)mTimeBaseNs);
-}
-
-EventMetricProducer::~EventMetricProducer() {
- VLOG("~EventMetricProducer() called");
-}
-
-bool EventMetricProducer::onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!MetricProducer::onConfigUpdatedLocked(
- config, configIndex, metricIndex, allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
- trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- return false;
- }
-
- const EventMetric& metric = config.event_metric(configIndex);
- int trackerIndex;
- // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return false;
- }
-
- if (metric.has_condition() &&
- !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, mConditionTrackerIndex,
- conditionToMetricMap)) {
- return false;
- }
- return true;
-}
-
-void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
- mProto->clear();
- StatsdStats::getInstance().noteBucketDropped(mMetricId);
-}
-
-void EventMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) {
-}
-
-std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& protoOutput) {
- size_t bufferSize = protoOutput.size();
-
- std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
-
- size_t pos = 0;
- sp<android::util::ProtoReader> reader = protoOutput.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((*buffer)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- return buffer;
-}
-
-void EventMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
- mProto->clear();
-}
-
-void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
- if (mProto->size() <= 0) {
- return;
- }
-
- size_t bufferSize = mProto->size();
- VLOG("metric %lld dump report now... proto size: %zu ",
- (long long)mMetricId, bufferSize);
- std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto);
-
- protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
- reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size());
-
- if (erase_data) {
- mProto->clear();
- }
-}
-
-void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
- const int64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
- mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
-}
-
-void EventMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const map<int, HashableDimensionKey>& statePrimaryKeys) {
- if (!condition) {
- return;
- }
-
- uint64_t wrapperToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs);
-
- uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
- event.ToProto(*mProto);
- mProto->end(eventToken);
- mProto->end(wrapperToken);
-}
-
-size_t EventMetricProducer::byteSizeLocked() const {
- return mProto->bytesWritten();
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
deleted file mode 100644
index 3347d7b6aab5..000000000000
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ /dev/null
@@ -1,103 +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.
- */
-
-#ifndef EVENT_METRIC_PRODUCER_H
-#define EVENT_METRIC_PRODUCER_H
-
-#include <unordered_map>
-
-#include <android/util/ProtoOutputStream.h>
-
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
-#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class EventMetricProducer : public MetricProducer {
-public:
- EventMetricProducer(
- const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const uint64_t protoHash, 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();
-
- MetricType getMetricType() const override {
- return METRIC_TYPE_EVENT;
- }
-
-private:
- void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
-
- void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) override;
- void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
-
- // Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
-
- // Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
-
- bool onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation) override;
-
- void dropDataLocked(const int64_t dropTimeNs) override;
-
- // Internal function to calculate the current used bytes.
- size_t byteSizeLocked() const override;
-
- void dumpStatesLocked(FILE* out, bool verbose) const override{};
-
- // Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream
- // is more space efficient than storing LogEvent.
- std::unique_ptr<android::util::ProtoOutputStream> mProto;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // EVENT_METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
deleted file mode 100644
index 2a37b587fbce..000000000000
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ /dev/null
@@ -1,675 +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.
-*/
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "GaugeMetricProducer.h"
-
-#include "guardrail/StatsdStats.h"
-#include "metrics/parsing_utils/metrics_manager_util.h"
-#include "stats_log_util.h"
-
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_FLOAT;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-using std::map;
-using std::string;
-using std::unordered_map;
-using std::vector;
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for StatsLogReport
-const int FIELD_ID_ID = 1;
-const int FIELD_ID_GAUGE_METRICS = 8;
-const int FIELD_ID_TIME_BASE = 9;
-const int FIELD_ID_BUCKET_SIZE = 10;
-const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_IS_ACTIVE = 14;
-// for GaugeMetricDataWrapper
-const int FIELD_ID_DATA = 1;
-const int FIELD_ID_SKIPPED = 2;
-// for SkippedBuckets
-const int FIELD_ID_SKIPPED_START_MILLIS = 3;
-const int FIELD_ID_SKIPPED_END_MILLIS = 4;
-const int FIELD_ID_SKIPPED_DROP_EVENT = 5;
-// for DumpEvent Proto
-const int FIELD_ID_BUCKET_DROP_REASON = 1;
-const int FIELD_ID_DROP_TIME = 2;
-// for GaugeMetricData
-const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_BUCKET_INFO = 3;
-const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-// for GaugeBucketInfo
-const int FIELD_ID_ATOM = 3;
-const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4;
-const int FIELD_ID_BUCKET_NUM = 6;
-const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 7;
-const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 8;
-
-GaugeMetricProducer::GaugeMetricProducer(
- const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const uint64_t protoHash, 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 unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
- const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
- protoHash, eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
- /*stateGroupMap=*/{}),
- mWhatMatcherIndex(whatMatcherIndex),
- mEventMatcherWizard(matcherWizard),
- mPullerManager(pullerManager),
- mPullTagId(pullTagId),
- mTriggerAtomId(triggerAtomId),
- mAtomId(atomId),
- mIsPulled(pullTagId != -1),
- mMinBucketSizeNs(metric.min_bucket_size_nanos()),
- mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
- : StatsdStats::kPullMaxDelayNs),
- mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
- StatsdStats::kAtomDimensionKeySizeLimitMap.end()
- ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
- : StatsdStats::kDimensionKeySizeSoftLimit),
- mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
- StatsdStats::kAtomDimensionKeySizeLimitMap.end()
- ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
- : StatsdStats::kDimensionKeySizeHardLimit),
- mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()),
- mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) {
- mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
- mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
- int64_t bucketSizeMills = 0;
- if (metric.has_bucket()) {
- bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
- } else {
- bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
- }
- mBucketSizeNs = bucketSizeMills * 1000000;
-
- mSamplingType = metric.sampling_type();
- if (!metric.gauge_fields_filter().include_all()) {
- translateFieldMatcher(metric.gauge_fields_filter().fields(), &mFieldMatchers);
- }
-
- if (metric.has_dimensions_in_what()) {
- translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
- mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
- }
-
- if (metric.links().size() > 0) {
- for (const auto& link : metric.links()) {
- Metric2Condition mc;
- mc.conditionId = link.condition();
- translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
- translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
- mMetric2ConditionLinks.push_back(mc);
- }
- mConditionSliced = true;
- }
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
-
- flushIfNeededLocked(startTimeNs);
- // Kicks off the puller immediately.
- if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
- mBucketSizeNs);
- }
-
- // Adjust start for partial first bucket and then pull if needed
- mCurrentBucketStartTimeNs = startTimeNs;
-
- VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
- (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
- mConditionSliced);
-}
-
-GaugeMetricProducer::~GaugeMetricProducer() {
- VLOG("~GaugeMetricProducer() called");
- if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
- }
-}
-
-bool GaugeMetricProducer::onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!MetricProducer::onConfigUpdatedLocked(
- config, configIndex, metricIndex, allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
- trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- return false;
- }
-
- const GaugeMetric& metric = config.gauge_metric(configIndex);
- // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap,
- trackerToMetricMap, mWhatMatcherIndex)) {
- return false;
- }
-
- // Need to update maps since the index changed, but mTriggerAtomId will not change.
- int triggerTrackerIndex;
- if (metric.has_trigger_event() &&
- !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
- /*enforceOneAtom=*/true, allAtomMatchingTrackers,
- newAtomMatchingTrackerMap, trackerToMetricMap,
- triggerTrackerIndex)) {
- return false;
- }
-
- if (metric.has_condition() &&
- !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, mConditionTrackerIndex,
- conditionToMetricMap)) {
- return false;
- }
- sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
- mEventMatcherWizard = matcherWizard;
- return true;
-}
-
-void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
- if (mCurrentSlicedBucket == nullptr ||
- mCurrentSlicedBucket->size() == 0) {
- return;
- }
-
- fprintf(out, "GaugeMetric %lld dimension size %lu\n", (long long)mMetricId,
- (unsigned long)mCurrentSlicedBucket->size());
- if (verbose) {
- for (const auto& it : *mCurrentSlicedBucket) {
- fprintf(out, "\t(what)%s\t(states)%s %d atoms\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getStateValuesKey().toString().c_str(), (int)it.second.size());
- }
- }
-}
-
-void GaugeMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
- flushIfNeededLocked(dumpTimeNs);
- mPastBuckets.clear();
- mSkippedBuckets.clear();
-}
-
-void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- VLOG("Gauge metric %lld report now...", (long long)mMetricId);
- if (include_current_partial_bucket) {
- flushLocked(dumpTimeNs);
- } else {
- flushIfNeededLocked(dumpTimeNs);
- }
-
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
-
- if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
- return;
- }
-
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
-
- // Fills the dimension path if not slicing by ALL.
- if (!mSliceByPositionALL) {
- if (!mDimensionsInWhat.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
- writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
- protoOutput->end(dimenPathToken);
- }
- }
-
- uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
-
- for (const auto& skippedBucket : mSkippedBuckets) {
- uint64_t wrapperToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
- (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs)));
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
- (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs)));
-
- for (const auto& dropEvent : skippedBucket.dropEvents) {
- uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SKIPPED_DROP_EVENT);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, (long long) (NanoToMillis(dropEvent.dropTimeNs)));
- protoOutput->end(dropEventToken);
- }
- protoOutput->end(wrapperToken);
- }
-
- for (const auto& pair : mPastBuckets) {
- const MetricDimensionKey& dimensionKey = pair.first;
-
- VLOG("Gauge dimension key %s", dimensionKey.toString().c_str());
- uint64_t wrapperToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
-
- // First fill dimension.
- if (mSliceByPositionALL) {
- uint64_t dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
- protoOutput->end(dimensionToken);
- } else {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
- FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- }
-
- // Then fill bucket_info (GaugeBucketInfo).
- for (const auto& bucket : pair.second) {
- uint64_t bucketInfoToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
-
- if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketStartNs));
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketEndNs));
- } else {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
- (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
- }
-
- if (!bucket.mGaugeAtoms.empty()) {
- for (const auto& atom : bucket.mGaugeAtoms) {
- uint64_t atomsToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ATOM);
- writeFieldValueTreeToStream(mAtomId, *(atom.mFields), protoOutput);
- protoOutput->end(atomsToken);
- }
- for (const auto& atom : bucket.mGaugeAtoms) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
- FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
- (long long)atom.mElapsedTimestampNs);
- }
- }
- protoOutput->end(bucketInfoToken);
- VLOG("Gauge \t bucket [%lld - %lld] includes %d atoms.",
- (long long)bucket.mBucketStartNs, (long long)bucket.mBucketEndNs,
- (int)bucket.mGaugeAtoms.size());
- }
- protoOutput->end(wrapperToken);
- }
- protoOutput->end(protoToken);
-
-
- if (erase_data) {
- mPastBuckets.clear();
- mSkippedBuckets.clear();
- }
-}
-
-void GaugeMetricProducer::prepareFirstBucketLocked() {
- if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
- }
-}
-
-void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
- bool triggerPuller = false;
- switch(mSamplingType) {
- // When the metric wants to do random sampling and there is already one gauge atom for the
- // current bucket, do not do it again.
- case GaugeMetric::RANDOM_ONE_SAMPLE: {
- triggerPuller = mCondition == ConditionState::kTrue && mCurrentSlicedBucket->empty();
- break;
- }
- case GaugeMetric::CONDITION_CHANGE_TO_TRUE: {
- triggerPuller = mCondition == ConditionState::kTrue;
- break;
- }
- case GaugeMetric::FIRST_N_SAMPLES: {
- triggerPuller = mCondition == ConditionState::kTrue;
- break;
- }
- default:
- break;
- }
- if (!triggerPuller) {
- return;
- }
- vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
- ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
- return;
- }
- const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
- if (pullDelayNs > mMaxPullDelayNs) {
- ALOGE("Pull finish too late for atom %d", mPullTagId);
- StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- return;
- }
- for (const auto& data : allData) {
- LogEvent localCopy = data->makeCopy();
- localCopy.setElapsedTimestampNs(timestampNs);
- if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
- MatchingState::kMatched) {
- onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
- }
- }
-}
-
-void GaugeMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
- MetricProducer::onActiveStateChangedLocked(eventTimeNs);
- if (ConditionState::kTrue != mCondition || !mIsPulled) {
- return;
- }
- if (mTriggerAtomId == -1 || (mIsActive && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE)) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
-
-}
-
-void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
- const int64_t eventTimeNs) {
- VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
-
- mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
- if (!mIsActive) {
- return;
- }
-
- flushIfNeededLocked(eventTimeNs);
- if (mIsPulled && mTriggerAtomId == -1) {
- pullAndMatchEventsLocked(eventTimeNs);
- } // else: Push mode. No need to proactively pull the gauge data.
-}
-
-void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTimeNs) {
- VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId,
- overallCondition);
- mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse;
- if (!mIsActive) {
- return;
- }
-
- flushIfNeededLocked(eventTimeNs);
- // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
- // pull for every dimension.
- if (mIsPulled && mTriggerAtomId == -1) {
- pullAndMatchEventsLocked(eventTimeNs);
- } // else: Push mode. No need to proactively pull the gauge data.
-}
-
-std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
- std::shared_ptr<vector<FieldValue>> gaugeFields;
- if (mFieldMatchers.size() > 0) {
- gaugeFields = std::make_shared<vector<FieldValue>>();
- filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get());
- } else {
- gaugeFields = std::make_shared<vector<FieldValue>>(event.getValues());
- }
- // Trim all dimension fields from output. Dimensions will appear in output report and will
- // benefit from dictionary encoding. For large pulled atoms, this can give the benefit of
- // optional repeated field.
- for (const auto& field : mDimensionsInWhat) {
- for (auto it = gaugeFields->begin(); it != gaugeFields->end();) {
- if (it->mField.matches(field)) {
- it = gaugeFields->erase(it);
- } else {
- it++;
- }
- }
- }
- return gaugeFields;
-}
-
-void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
- bool pullSuccess, int64_t originalPullTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!pullSuccess || allData.size() == 0) {
- return;
- }
- const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
- if (pullDelayNs > mMaxPullDelayNs) {
- ALOGE("Pull finish too late for atom %d", mPullTagId);
- StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- return;
- }
- for (const auto& data : allData) {
- if (mEventMatcherWizard->matchLogEvent(
- *data, mWhatMatcherIndex) == MatchingState::kMatched) {
- onMatchedLogEventLocked(mWhatMatcherIndex, *data);
- }
- }
-}
-
-bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
- if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
- return false;
- }
- // 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedBucket->size() > mDimensionSoftLimit - 1) {
- size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > mDimensionHardLimit) {
- ALOGE("GaugeMetric %lld dropping data for dimension key %s",
- (long long)mMetricId, newKey.toString().c_str());
- StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
- return true;
- }
- }
-
- return false;
-}
-
-void GaugeMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const map<int, HashableDimensionKey>& statePrimaryKeys) {
- if (condition == false) {
- return;
- }
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
- if (eventTimeNs < mCurrentBucketStartTimeNs) {
- VLOG("Gauge Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
- (long long)mCurrentBucketStartTimeNs);
- return;
- }
- flushIfNeededLocked(eventTimeNs);
-
- if (mTriggerAtomId == event.GetTagId()) {
- pullAndMatchEventsLocked(eventTimeNs);
- return;
- }
-
- // When gauge metric wants to randomly sample the output atom, we just simply use the first
- // gauge in the given bucket.
- if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end() &&
- mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- return;
- }
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
- if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
- return;
- }
-
- const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event);
- GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs);
- (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
- // Anomaly detection on gauge metric only works when there is one numeric
- // field specified.
- if (mAnomalyTrackers.size() > 0) {
- if (gaugeAtom.mFields->size() == 1) {
- const Value& value = gaugeAtom.mFields->begin()->mValue;
- long gaugeVal = 0;
- if (value.getType() == INT) {
- gaugeVal = (long)value.int_value;
- } else if (value.getType() == LONG) {
- gaugeVal = value.long_value;
- }
- for (auto& tracker : mAnomalyTrackers) {
- tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId,
- eventKey, gaugeVal);
- }
- }
- }
-}
-
-void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() {
- for (const auto& slice : *mCurrentSlicedBucket) {
- if (slice.second.empty()) {
- continue;
- }
- const Value& value = slice.second.front().mFields->front().mValue;
- long gaugeVal = 0;
- if (value.getType() == INT) {
- gaugeVal = (long)value.int_value;
- } else if (value.getType() == LONG) {
- gaugeVal = value.long_value;
- }
- (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal;
- }
-}
-
-void GaugeMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
- flushIfNeededLocked(dropTimeNs);
- StatsdStats::getInstance().noteBucketDropped(mMetricId);
- mPastBuckets.clear();
-}
-
-// When a new matched event comes in, we check if event falls into the current
-// bucket. If not, flush the old counter to past buckets and initialize the new
-// bucket.
-// if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
-// the GaugeMetricProducer while holding the lock.
-void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
- int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
-
- if (eventTimeNs < currentBucketEndTimeNs) {
- VLOG("Gauge eventTime is %lld, less than next bucket start time %lld",
- (long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
- return;
- }
-
- // Adjusts the bucket start and end times.
- int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
- int64_t nextBucketNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
- flushCurrentBucketLocked(eventTimeNs, nextBucketNs);
-
- mCurrentBucketNum += numBucketsForward;
- VLOG("Gauge metric %lld: new bucket start time: %lld", (long long)mMetricId,
- (long long)mCurrentBucketStartTimeNs);
-}
-
-void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {
- int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
- int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
-
- GaugeBucket info;
- info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = bucketEndTime;
-
- // Add bucket to mPastBuckets if bucket is large enough.
- // Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets.
- bool isBucketLargeEnough = info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
- if (isBucketLargeEnough) {
- for (const auto& slice : *mCurrentSlicedBucket) {
- info.mGaugeAtoms = slice.second;
- auto& bucketList = mPastBuckets[slice.first];
- bucketList.push_back(info);
- VLOG("Gauge gauge metric %lld, dump key value: %s", (long long)mMetricId,
- slice.first.toString().c_str());
- }
- } else {
- mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
- if (!maxDropEventsReached()) {
- mCurrentSkippedBucket.dropEvents.emplace_back(
- buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
- }
- mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
- }
-
- // If we have anomaly trackers, we need to update the partial bucket values.
- if (mAnomalyTrackers.size() > 0) {
- updateCurrentSlicedBucketForAnomaly();
-
- if (eventTimeNs > fullBucketEndTimeNs) {
- // This is known to be a full bucket, so send this data to the anomaly tracker.
- for (auto& tracker : mAnomalyTrackers) {
- tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum);
- }
- mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
- }
- }
-
- StatsdStats::getInstance().noteBucketCount(mMetricId);
- mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
- mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
- mCurrentSkippedBucket.reset();
-}
-
-size_t GaugeMetricProducer::byteSizeLocked() const {
- size_t totalSize = 0;
- for (const auto& pair : mPastBuckets) {
- for (const auto& bucket : pair.second) {
- totalSize += bucket.mGaugeAtoms.size() * sizeof(GaugeAtom);
- for (const auto& atom : bucket.mGaugeAtoms) {
- if (atom.mFields != nullptr) {
- totalSize += atom.mFields->size() * sizeof(FieldValue);
- }
- }
- }
- }
- return totalSize;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
deleted file mode 100644
index 9bdaac96c9ef..000000000000
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ /dev/null
@@ -1,234 +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.
- */
-
-#pragma once
-
-#include <unordered_map>
-
-#include <android/util/ProtoOutputStream.h>
-#include <gtest/gtest_prod.h>
-#include "../condition/ConditionTracker.h"
-#include "../external/PullDataReceiver.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/matcher_util.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "../stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-struct GaugeAtom {
- GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
- : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
- }
- std::shared_ptr<vector<FieldValue>> mFields;
- int64_t mElapsedTimestampNs;
-};
-
-struct GaugeBucket {
- int64_t mBucketStartNs;
- int64_t mBucketEndNs;
- std::vector<GaugeAtom> mGaugeAtoms;
-};
-
-typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
- DimToGaugeAtomsMap;
-
-// This gauge metric producer first register the puller to automatically pull the gauge at the
-// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
-// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
-// producer always reports the gauge at the earliest time of the bucket when the condition is met.
-class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
-public:
- GaugeMetricProducer(
- const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
- 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 = {});
-
- virtual ~GaugeMetricProducer();
-
- // Handles when the pulled data arrives.
- void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
- bool pullSuccess, int64_t originalPullTimeNs) override;
-
- // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
- void notifyAppUpgrade(const int64_t& eventTimeNs) override {
- std::lock_guard<std::mutex> lock(mMutex);
-
- if (!mSplitBucketForAppUpgrade) {
- return;
- }
- flushLocked(eventTimeNs);
- if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
- };
-
- // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
- void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
- std::lock_guard<std::mutex> lock(mMutex);
-
- flushLocked(eventTimeNs);
- if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
- };
-
- MetricType getMetricType() const override {
- return METRIC_TYPE_GAUGE;
- }
-
-protected:
- void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
-
-private:
- void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) override;
- void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
-
- // Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
-
- // Internal interface to handle active state change.
- void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
-
- // Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
-
- // Internal function to calculate the current used bytes.
- size_t byteSizeLocked() const override;
-
- void dumpStatesLocked(FILE* out, bool verbose) const override;
-
- void dropDataLocked(const int64_t dropTimeNs) override;
-
- // Util function to flush the old packet.
- void flushIfNeededLocked(const int64_t& eventTime) override;
-
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
-
- void prepareFirstBucketLocked() override;
-
- void pullAndMatchEventsLocked(const int64_t timestampNs);
-
- bool onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation) override;
-
- int mWhatMatcherIndex;
-
- sp<EventMatcherWizard> mEventMatcherWizard;
-
- sp<StatsPullerManager> mPullerManager;
- // tagId for pulled data. -1 if this is not pulled
- const int mPullTagId;
-
- // tagId for atoms that trigger the pulling, if any
- const int mTriggerAtomId;
-
- // tagId for output atom
- const int mAtomId;
-
- // if this is pulled metric
- const bool mIsPulled;
-
- // Save the past buckets and we can clear when the StatsLogReport is dumped.
- std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
-
- // The current partial bucket.
- std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
-
- // The current full bucket for anomaly detection. This is updated to the latest value seen for
- // this slice (ie, for partial buckets, we use the last partial bucket in this full bucket).
- std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
-
- const int64_t mMinBucketSizeNs;
-
- // Translate Atom based bucket to single numeric value bucket for anomaly and updates the map
- // for each slice with the latest value.
- void updateCurrentSlicedBucketForAnomaly();
-
- // Allowlist of fields to report. Empty means all are reported.
- std::vector<Matcher> mFieldMatchers;
-
- GaugeMetric::SamplingType mSamplingType;
-
- const int64_t mMaxPullDelayNs;
-
- // apply an allowlist on the original input
- std::shared_ptr<vector<FieldValue>> getGaugeFields(const LogEvent& event);
-
- // Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const MetricDimensionKey& newKey);
-
- static const size_t kBucketSize = sizeof(GaugeBucket{});
-
- const size_t mDimensionSoftLimit;
-
- const size_t mDimensionHardLimit;
-
- const size_t mGaugeAtomsPerDimensionLimit;
-
- const bool mSplitBucketForAppUpgrade;
-
- FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled);
- FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
- FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
- FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
- FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput);
-
- FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
- FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
deleted file mode 100644
index 5b321a0e3d60..000000000000
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ /dev/null
@@ -1,355 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "MetricProducer.h"
-
-#include "../guardrail/StatsdStats.h"
-#include "metrics/parsing_utils/metrics_manager_util.h"
-#include "state/StateTracker.h"
-
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_ENUM;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::ProtoOutputStream;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-
-// for ActiveMetric
-const int FIELD_ID_ACTIVE_METRIC_ID = 1;
-const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
-
-// for ActiveEventActivation
-const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
-const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
-const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
-
-MetricProducer::MetricProducer(
- const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
- const int conditionIndex, const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& wizard, const uint64_t protoHash,
- 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)
- : mMetricId(metricId),
- mProtoHash(protoHash),
- mConfigKey(key),
- mTimeBaseNs(timeBaseNs),
- mCurrentBucketStartTimeNs(timeBaseNs),
- mCurrentBucketNum(0),
- mCondition(initialCondition(conditionIndex, initialConditionCache)),
- mConditionTrackerIndex(conditionIndex),
- mConditionSliced(false),
- mWizard(wizard),
- mContainANYPositionInDimensionsInWhat(false),
- mSliceByPositionALL(false),
- mHasLinksToAllConditionDimensionsInTracker(false),
- mEventActivationMap(eventActivationMap),
- mEventDeactivationMap(eventDeactivationMap),
- mIsActive(mEventActivationMap.empty()),
- mSlicedStateAtoms(slicedStateAtoms),
- mStateGroupMap(stateGroupMap) {
-}
-
-bool MetricProducer::onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- sp<ConditionWizard> tmpWizard = mWizard;
- mWizard = wizard;
-
- unordered_map<int, shared_ptr<Activation>> newEventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap;
- if (!handleMetricActivationOnConfigUpdate(
- config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap,
- newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap,
- newEventDeactivationMap)) {
- return false;
- }
- mEventActivationMap = newEventActivationMap;
- mEventDeactivationMap = newEventDeactivationMap;
- mAnomalyTrackers.clear();
- return true;
-}
-
-void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
- if (!mIsActive) {
- return;
- }
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
- // this is old event, maybe statsd restarted?
- if (eventTimeNs < mTimeBaseNs) {
- return;
- }
-
- bool condition;
- ConditionKey conditionKey;
- if (mConditionSliced) {
- for (const auto& link : mMetric2ConditionLinks) {
- getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
- }
- auto conditionState =
- mWizard->query(mConditionTrackerIndex, conditionKey,
- !mHasLinksToAllConditionDimensionsInTracker);
- condition = (conditionState == ConditionState::kTrue);
- } else {
- // TODO: The unknown condition state is not handled here, we should fix it.
- condition = mCondition == ConditionState::kTrue;
- }
-
- // Stores atom id to primary key pairs for each state atom that the metric is
- // sliced by.
- std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
-
- // For states with primary fields, use MetricStateLinks to get the primary
- // field values from the log event. These values will form a primary key
- // that will be used to query StateTracker for the correct state value.
- for (const auto& stateLink : mMetric2StateLinks) {
- getDimensionForState(event.getValues(), stateLink,
- &statePrimaryKeys[stateLink.stateAtomId]);
- }
-
- // For each sliced state, query StateTracker for the state value using
- // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
- //
- // Expected functionality: for any case where the MetricStateLinks are
- // initialized incorrectly (ex. # of state links != # of primary fields, no
- // links are provided for a state with primary fields, links are provided
- // in the wrong order, etc.), StateTracker will simply return kStateUnknown
- // when queried using an incorrect key.
- HashableDimensionKey stateValuesKey;
- for (auto atomId : mSlicedStateAtoms) {
- FieldValue value;
- if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
- // found a primary key for this state, query using the key
- queryStateValue(atomId, statePrimaryKeys[atomId], &value);
- } else {
- // if no MetricStateLinks exist for this state atom,
- // query using the default dimension key (empty HashableDimensionKey)
- queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
- }
- mapStateValue(atomId, &value);
- stateValuesKey.addValue(value);
- }
-
- HashableDimensionKey dimensionInWhat;
- filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
- onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
- statePrimaryKeys);
-}
-
-bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
- bool isActive = mEventActivationMap.empty();
- for (auto& it : mEventActivationMap) {
- if (it.second->state == ActivationState::kActive &&
- elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
- it.second->state = ActivationState::kNotActive;
- }
- if (it.second->state == ActivationState::kActive) {
- isActive = true;
- }
- }
- return isActive;
-}
-
-void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mIsActive) {
- return;
- }
- mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
- if (!mIsActive) {
- onActiveStateChangedLocked(elapsedTimestampNs);
- }
-}
-
-void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
- auto it = mEventActivationMap.find(activationTrackerIndex);
- if (it == mEventActivationMap.end()) {
- return;
- }
- auto& activation = it->second;
- if (ACTIVATE_ON_BOOT == activation->activationType) {
- if (ActivationState::kNotActive == activation->state) {
- activation->state = ActivationState::kActiveOnBoot;
- }
- // If the Activation is already active or set to kActiveOnBoot, do nothing.
- return;
- }
- activation->start_ns = elapsedTimestampNs;
- activation->state = ActivationState::kActive;
- bool oldActiveState = mIsActive;
- mIsActive = true;
- if (!oldActiveState) { // Metric went from not active to active.
- onActiveStateChangedLocked(elapsedTimestampNs);
- }
-}
-
-void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
- auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
- if (it == mEventDeactivationMap.end()) {
- return;
- }
- for (auto activationToCancelIt : it->second) {
- activationToCancelIt->state = ActivationState::kNotActive;
- }
-}
-
-void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
- int64_t currentTimeNs) {
- if (mEventActivationMap.size() == 0) {
- return;
- }
- for (int i = 0; i < activeMetric.activation_size(); i++) {
- const auto& activeEventActivation = activeMetric.activation(i);
- auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
- if (it == mEventActivationMap.end()) {
- ALOGE("Saved event activation not found");
- continue;
- }
- auto& activation = it->second;
- // If the event activation does not have a state, assume it is active.
- if (!activeEventActivation.has_state() ||
- activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
- // We don't want to change the ttl for future activations, so we set the start_ns
- // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
- activation->start_ns =
- currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
- activation->state = ActivationState::kActive;
- mIsActive = true;
- } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
- activation->state = ActivationState::kActiveOnBoot;
- }
- }
-}
-
-void MetricProducer::writeActiveMetricToProtoOutputStream(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
- for (auto& it : mEventActivationMap) {
- const int atom_matcher_index = it.first;
- const std::shared_ptr<Activation>& activation = it.second;
-
- if (ActivationState::kNotActive == activation->state ||
- (ActivationState::kActive == activation->state &&
- activation->start_ns + activation->ttl_ns < currentTimeNs)) {
- continue;
- }
-
- const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ACTIVE_METRIC_ACTIVATION);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
- atom_matcher_index);
- if (ActivationState::kActive == activation->state) {
- const int64_t remainingTtlNs =
- activation->start_ns + activation->ttl_ns - currentTimeNs;
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
- (long long)remainingTtlNs);
- proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
- ActiveEventActivation::ACTIVE);
-
- } else if (ActivationState::kActiveOnBoot == activation->state) {
- if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
- proto->write(
- FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
- (long long)activation->ttl_ns);
- proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
- ActiveEventActivation::ACTIVE);
- } else if (reason == STATSCOMPANION_DIED) {
- // We are saving because of system server death, not due to a device shutdown.
- // Next time we load, we do not want to activate metrics that activate on boot.
- proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
- ActiveEventActivation::ACTIVATE_ON_BOOT);
- }
- }
- proto->end(activationToken);
- }
-}
-
-void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
- FieldValue* value) {
- if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
- value->mValue = Value(StateTracker::kStateUnknown);
- value->mField.setTag(atomId);
- ALOGW("StateTracker not found for state atom %d", atomId);
- return;
- }
-}
-
-void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
- // check if there is a state map for this atom
- auto atomIt = mStateGroupMap.find(atomId);
- if (atomIt == mStateGroupMap.end()) {
- return;
- }
- auto valueIt = atomIt->second.find(value->mValue.int_value);
- if (valueIt == atomIt->second.end()) {
- // state map exists, but value was not put in a state group
- // so set mValue to kStateUnknown
- // TODO(tsaichristine): handle incomplete state maps
- value->mValue.setInt(StateTracker::kStateUnknown);
- } else {
- // set mValue to group_id
- value->mValue.setLong(valueIt->second);
- }
-}
-
-HashableDimensionKey MetricProducer::getUnknownStateKey() {
- HashableDimensionKey stateKey;
- for (auto atom : mSlicedStateAtoms) {
- FieldValue fieldValue;
- fieldValue.mField.setTag(atom);
- fieldValue.mValue.setInt(StateTracker::kStateUnknown);
- stateKey.addValue(fieldValue);
- }
- return stateKey;
-}
-
-DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) {
- DropEvent event;
- event.reason = reason;
- event.dropTimeNs = dropTimeNs;
- return event;
-}
-
-bool MetricProducer::maxDropEventsReached() {
- return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
deleted file mode 100644
index 0dc8edae8056..000000000000
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ /dev/null
@@ -1,582 +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.
- */
-
-#ifndef METRIC_PRODUCER_H
-#define METRIC_PRODUCER_H
-
-#include <frameworks/base/cmds/statsd/src/active_config_list.pb.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/EventMatcherWizard.h"
-#include "matchers/matcher_util.h"
-#include "packages/PackageInfoListener.h"
-#include "state/StateListener.h"
-#include "state/StateManager.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Keep this in sync with DumpReportReason enum in stats_log.proto
-enum DumpReportReason {
- DEVICE_SHUTDOWN = 1,
- CONFIG_UPDATED = 2,
- CONFIG_REMOVED = 3,
- GET_DATA_CALLED = 4,
- ADB_DUMP = 5,
- CONFIG_RESET = 6,
- STATSCOMPANION_DIED = 7,
- TERMINATION_SIGNAL_RECEIVED = 8
-};
-
-// If the metric has no activation requirement, it will be active once the metric producer is
-// created.
-// If the metric needs to be activated by atoms, the metric producer will start
-// with kNotActive state, turn to kActive or kActiveOnBoot when the activation event arrives, become
-// kNotActive when it reaches the duration limit (timebomb). If the activation event arrives again
-// before or after it expires, the event producer will be re-activated and ttl will be reset.
-enum ActivationState {
- kNotActive = 0,
- kActive = 1,
- kActiveOnBoot = 2,
-};
-
-enum DumpLatency {
- // In some cases, we only have a short time range to do the dump, e.g. statsd is being killed.
- // We might be able to return all the data in this mode. For instance, pull metrics might need
- // to be pulled when the current bucket is requested.
- FAST = 1,
- // In other cases, it is fine for a dump to take more than a few milliseconds, e.g. config
- // updates.
- NO_TIME_CONSTRAINTS = 2
-};
-
-// Keep this in sync with BucketDropReason enum in stats_log.proto
-enum BucketDropReason {
- // For ValueMetric, a bucket is dropped during a dump report request iff
- // current bucket should be included, a pull is needed (pulled metric and
- // condition is true), and we are under fast time constraints.
- DUMP_REPORT_REQUESTED = 1,
- EVENT_IN_WRONG_BUCKET = 2,
- CONDITION_UNKNOWN = 3,
- PULL_FAILED = 4,
- PULL_DELAYED = 5,
- DIMENSION_GUARDRAIL_REACHED = 6,
- MULTIPLE_BUCKETS_SKIPPED = 7,
- // Not an invalid bucket case, but the bucket is dropped.
- BUCKET_TOO_SMALL = 8,
- // Not an invalid bucket case, but the bucket is skipped.
- NO_DATA = 9
-};
-
-enum MetricType {
- METRIC_TYPE_EVENT = 1,
- METRIC_TYPE_COUNT = 2,
- METRIC_TYPE_DURATION = 3,
- METRIC_TYPE_GAUGE = 4,
- METRIC_TYPE_VALUE = 5,
-};
-struct Activation {
- Activation(const ActivationType& activationType, const int64_t ttlNs)
- : ttl_ns(ttlNs),
- start_ns(0),
- state(ActivationState::kNotActive),
- activationType(activationType) {}
-
- const int64_t ttl_ns;
- int64_t start_ns;
- ActivationState state;
- const ActivationType activationType;
-};
-
-struct DropEvent {
- // Reason for dropping the bucket and/or marking the bucket invalid.
- BucketDropReason reason;
- // The timestamp of the drop event.
- int64_t dropTimeNs;
-};
-
-struct SkippedBucket {
- // Start time of the dropped bucket.
- int64_t bucketStartTimeNs;
- // End time of the dropped bucket.
- int64_t bucketEndTimeNs;
- // List of events that invalidated this bucket.
- std::vector<DropEvent> dropEvents;
-
- void reset() {
- bucketStartTimeNs = 0;
- bucketEndTimeNs = 0;
- dropEvents.clear();
- }
-};
-
-// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
-// 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 android::RefBase, public virtual StateListener {
-public:
- MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
- const int conditionIndex, const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& wizard, const uint64_t protoHash,
- 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 ~MetricProducer(){};
-
- ConditionState initialCondition(const int conditionIndex,
- const vector<ConditionState>& initialConditionCache) const {
- return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue;
- }
-
- // Update appropriate state on config updates. Primarily, all indices need to be updated.
- // This metric and all of its dependencies are guaranteed to be preserved across the update.
- // This function also updates several maps used by metricsManager.
- // This function clears all anomaly trackers. All anomaly trackers need to be added again.
- bool onConfigUpdated(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation) {
- std::lock_guard<std::mutex> lock(mMutex);
- return onConfigUpdatedLocked(config, configIndex, metricIndex, allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
- matcherWizard, allConditionTrackers, conditionTrackerMap,
- wizard, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- };
-
- /**
- * Force a partial bucket split on app upgrade
- */
- virtual void notifyAppUpgrade(const int64_t& eventTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- flushLocked(eventTimeNs);
- };
-
- void notifyAppRemoved(const int64_t& eventTimeNs) {
- // Force buckets to split on removal also.
- notifyAppUpgrade(eventTimeNs);
- };
-
- /**
- * Force a partial bucket split on boot complete.
- */
- virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- flushLocked(eventTimeNs);
- }
- // Consume the parsed stats log entry that already matched the "what" of the metric.
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
- std::lock_guard<std::mutex> lock(mMutex);
- onMatchedLogEventLocked(matcherIndex, event);
- }
-
- void onConditionChanged(const bool condition, const int64_t eventTime) {
- std::lock_guard<std::mutex> lock(mMutex);
- onConditionChangedLocked(condition, eventTime);
- }
-
- void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) {
- std::lock_guard<std::mutex> lock(mMutex);
- onSlicedConditionMayChangeLocked(overallCondition, eventTime);
- }
-
- bool isConditionSliced() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mConditionSliced;
- };
-
- void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const FieldValue& oldState,
- const FieldValue& 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,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) {
- std::lock_guard<std::mutex> lock(mMutex);
- return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data,
- dumpLatency, str_set, protoOutput);
- }
-
- virtual bool onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
- void clearPastBuckets(const int64_t dumpTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- return clearPastBucketsLocked(dumpTimeNs);
- }
-
- void prepareFirstBucket() {
- std::lock_guard<std::mutex> lock(mMutex);
- prepareFirstBucketLocked();
- }
-
- // Returns the memory in bytes currently used to store this metric's data. Does not change
- // state.
- size_t byteSize() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return byteSizeLocked();
- }
-
- void dumpStates(FILE* out, bool verbose) const {
- std::lock_guard<std::mutex> lock(mMutex);
- dumpStatesLocked(out, verbose);
- }
-
- // Let MetricProducer drop in-memory data to save memory.
- // We still need to keep future data valid and anomaly tracking work, which means we will
- // have to flush old data, informing anomaly trackers then safely drop old data.
- // We still keep current bucket data for future metrics' validity.
- void dropData(const int64_t dropTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- dropDataLocked(dropTimeNs);
- }
-
- void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- loadActiveMetricLocked(activeMetric, currentTimeNs);
- }
-
- void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- activateLocked(activationTrackerIndex, elapsedTimestampNs);
- }
-
- void cancelEventActivation(int deactivationTrackerIndex) {
- std::lock_guard<std::mutex> lock(mMutex);
- cancelEventActivationLocked(deactivationTrackerIndex);
- }
-
- bool isActive() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return isActiveLocked();
- }
-
- void flushIfExpire(int64_t elapsedTimestampNs);
-
- void writeActiveMetricToProtoOutputStream(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
-
- // Start: getters/setters
- inline int64_t getMetricId() const {
- return mMetricId;
- }
-
- inline uint64_t getProtoHash() const {
- return mProtoHash;
- }
-
- virtual MetricType getMetricType() const = 0;
-
- // For test only.
- inline int64_t getCurrentBucketNum() const {
- return mCurrentBucketNum;
- }
-
- int64_t getBucketSizeInNs() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mBucketSizeNs;
- }
-
- inline const std::vector<int> getSlicedStateAtoms() {
- std::lock_guard<std::mutex> lock(mMutex);
- return mSlicedStateAtoms;
- }
-
- /* Adds an AnomalyTracker and returns it. */
- virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
- const sp<AlarmMonitor>& anomalyAlarmMonitor) {
- std::lock_guard<std::mutex> lock(mMutex);
- sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
- mAnomalyTrackers.push_back(anomalyTracker);
- return anomalyTracker;
- }
-
- /* Adds an AnomalyTracker that has already been created */
- virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
- std::lock_guard<std::mutex> lock(mMutex);
- mAnomalyTrackers.push_back(anomalyTracker);
- }
- // End: getters/setters
-protected:
- /**
- * Flushes the current bucket if the eventTime is after the current bucket's end time.
- */
- virtual void flushIfNeededLocked(const int64_t& eventTime){};
-
- /**
- * For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
- * we need to be able to flush the current buckets on demand (ie, end the current bucket and
- * start new bucket). If this function is called when eventTimeNs is greater than the current
- * bucket's end timestamp, than we flush up to the end of the latest full bucket; otherwise,
- * we assume that we want to flush a partial bucket. The bucket start timestamp and bucket
- * number are not changed by this function. This method should only be called by
- * flushIfNeededLocked or flushLocked or the app upgrade handler; the caller MUST update the
- * bucket timestamp and bucket number as needed.
- */
- virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {};
-
- /**
- * Flushes all the data including the current partial bucket.
- */
- virtual void flushLocked(const int64_t& eventTimeNs) {
- flushIfNeededLocked(eventTimeNs);
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- };
-
- /*
- * Individual metrics can implement their own business logic here. All pre-processing is done.
- *
- * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
- * DurationMetric, because it has start/stop/stop_all 3 matchers.
- * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
- * dimensions, it will be DEFAULT_DIMENSION_KEY
- * [conditionKey]: the keys of conditions which should be used to query the condition for this
- * target event (from MetricConditionLink). This is passed to individual metrics
- * because DurationMetric needs it to be cached.
- * [condition]: whether condition is met. If condition is sliced, this is the result coming from
- * query with ConditionWizard; If condition is not sliced, this is the
- * nonSlicedCondition.
- * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
- */
- virtual void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const map<int, HashableDimensionKey>& statePrimaryKeys) = 0;
-
- // Consume the parsed stats log entry that already matched the "what" of the metric.
- virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
- virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
- virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) = 0;
- virtual void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) = 0;
- virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
- virtual void prepareFirstBucketLocked(){};
- virtual size_t byteSizeLocked() const = 0;
- virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
- virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
- void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
- void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
- void cancelEventActivationLocked(int deactivationTrackerIndex);
-
- bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
-
- virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) {
- if (!mIsActive) {
- flushLocked(eventTimeNs);
- }
- }
-
- inline bool isActiveLocked() const {
- return mIsActive;
- }
-
- // Convenience to compute the current bucket's end time, which is always aligned with the
- // start time of the metric.
- int64_t getCurrentBucketEndTimeNs() const {
- return mTimeBaseNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
- }
-
- int64_t getBucketNumFromEndTimeNs(const int64_t endNs) {
- return (endNs - mTimeBaseNs) / mBucketSizeNs - 1;
- }
-
- // Query StateManager for original state value using the queryKey.
- // The field and value are output.
- void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
- FieldValue* value);
-
- // If a state map exists for the given atom, replace the original state
- // value with the group id mapped to the value.
- // If no state map exists, keep the original state value.
- void mapStateValue(const int32_t atomId, FieldValue* value);
-
- // Returns a HashableDimensionKey with unknown state value for each state
- // atom.
- HashableDimensionKey getUnknownStateKey();
-
- DropEvent buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason);
-
- // Returns true if the number of drop events in the current bucket has
- // exceeded the maximum number allowed, which is currently capped at 10.
- bool maxDropEventsReached();
-
- const int64_t mMetricId;
-
- // Hash of the Metric's proto bytes from StatsdConfig, including any activations.
- // Used to determine if the definition of this metric has changed across a config update.
- const uint64_t mProtoHash;
-
- const ConfigKey mConfigKey;
-
- // The time when this metric producer was first created. The end time for the current bucket
- // can be computed from this based on mCurrentBucketNum.
- int64_t mTimeBaseNs;
-
- // Start time may not be aligned with the start of statsd if there is an app upgrade in the
- // middle of a bucket.
- int64_t mCurrentBucketStartTimeNs;
-
- // Used by anomaly detector to track which bucket we are in. This is not sent with the produced
- // report.
- int64_t mCurrentBucketNum;
-
- int64_t mBucketSizeNs;
-
- ConditionState mCondition;
-
- int mConditionTrackerIndex;
-
- bool mConditionSliced;
-
- sp<ConditionWizard> mWizard;
-
- bool mContainANYPositionInDimensionsInWhat;
-
- bool mSliceByPositionALL;
-
- vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
-
- // True iff the metric to condition links cover all dimension fields in the condition tracker.
- // This field is always false for combinational condition trackers.
- bool mHasLinksToAllConditionDimensionsInTracker;
-
- std::vector<Metric2Condition> mMetric2ConditionLinks;
-
- std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
-
- mutable std::mutex mMutex;
-
- // When the metric producer has multiple activations, these activations are ORed to determine
- // whether the metric producer is ready to generate metrics.
- std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
-
- // Maps index of atom matcher for deactivation to a list of Activation structs.
- std::unordered_map<int, std::vector<std::shared_ptr<Activation>>> mEventDeactivationMap;
-
- bool mIsActive;
-
- // The slice_by_state atom ids defined in statsd_config.
- const std::vector<int32_t> mSlicedStateAtoms;
-
- // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
- const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
-
- // MetricStateLinks defined in statsd_config that link fields in the state
- // atom to fields in the "what" atom.
- std::vector<Metric2State> mMetric2StateLinks;
-
- SkippedBucket mCurrentSkippedBucket;
- // Buckets that were invalidated and had their data dropped.
- std::vector<SkippedBucket> mSkippedBuckets;
-
- FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
- FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
- FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
-
- FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
- FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
- FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
- FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
- FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
- FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
-
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
-
- FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
- FRIEND_TEST(StatsLogProcessorTest,
- TestActivationOnBootMultipleActivationsDifferentActivationTypes);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
-
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
- FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
-
- FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // METRIC_PRODUCER_H
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
deleted file mode 100644
index f9b0a1030eee..000000000000
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ /dev/null
@@ -1,773 +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.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "MetricsManager.h"
-
-#include <private/android_filesystem_config.h>
-
-#include "CountMetricProducer.h"
-#include "condition/CombinationConditionTracker.h"
-#include "condition/SimpleConditionTracker.h"
-#include "guardrail/StatsdStats.h"
-#include "matchers/CombinationAtomMatchingTracker.h"
-#include "matchers/SimpleAtomMatchingTracker.h"
-#include "parsing_utils/config_update_utils.h"
-#include "parsing_utils/metrics_manager_util.h"
-#include "state/StateManager.h"
-#include "stats_log_util.h"
-#include "stats_util.h"
-#include "statslog_statsd.h"
-
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-
-using std::set;
-using std::string;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const int FIELD_ID_METRICS = 1;
-const int FIELD_ID_ANNOTATIONS = 7;
-const int FIELD_ID_ANNOTATIONS_INT64 = 1;
-const int FIELD_ID_ANNOTATIONS_INT32 = 2;
-
-// for ActiveConfig
-const int FIELD_ID_ACTIVE_CONFIG_ID = 1;
-const int FIELD_ID_ACTIVE_CONFIG_UID = 2;
-const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3;
-
-MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
- const int64_t timeBaseNs, const int64_t currentTimeNs,
- const sp<UidMap>& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor)
- : mConfigKey(key),
- mUidMap(uidMap),
- mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
- mTtlEndNs(-1),
- mLastReportTimeNs(currentTimeNs),
- mLastReportWallClockNs(getWallClockNs()),
- mPullerManager(pullerManager),
- mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
- config.whitelisted_atom_ids().end()),
- mShouldPersistHistory(config.persist_locally()) {
- // Init the ttl end timestamp.
- refreshTtl(timeBaseNs);
-
- mConfigValid = initStatsdConfig(
- key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
- mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
- mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap,
- mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
- mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation,
- mStateProtoHashes, mNoReportMetricIds);
-
- mHashStringsInReport = config.hash_strings_in_metric_report();
- mVersionStringsInReport = config.version_strings_in_metric_report();
- mInstallerInReport = config.installer_in_metric_report();
-
- createAllLogSourcesFromConfig(config);
- mPullerManager->RegisterPullUidProvider(mConfigKey, this);
-
- // Store the sub-configs used.
- for (const auto& annotation : config.annotation()) {
- mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
- }
- verifyGuardrailsAndUpdateStatsdStats();
- initializeConfigActiveStatus();
-}
-
-MetricsManager::~MetricsManager() {
- for (auto it : mAllMetricProducers) {
- for (int atomId : it->getSlicedStateAtoms()) {
- StateManager::getInstance().unregisterListener(atomId, it);
- }
- }
- mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
-
- VLOG("~MetricsManager()");
-}
-
-bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor) {
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- vector<sp<ConditionTracker>> newConditionTrackers;
- unordered_map<int64_t, int> newConditionTrackerMap;
- map<int64_t, uint64_t> newStateProtoHashes;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<AnomalyTracker>> newAnomalyTrackers;
- unordered_map<int64_t, int> newAlertTrackerMap;
- vector<sp<AlarmTracker>> newPeriodicAlarmTrackers;
- mTagIds.clear();
- mConditionToMetricMap.clear();
- mTrackerToMetricMap.clear();
- mTrackerToConditionMap.clear();
- mActivationAtomTrackerToMetricMap.clear();
- mDeactivationAtomTrackerToMetricMap.clear();
- mMetricIndexesWithActivation.clear();
- mNoReportMetricIds.clear();
- mConfigValid = updateStatsdConfig(
- mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
- mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
- mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, mTagIds,
- newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers,
- newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers,
- newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap,
- mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
- mDeactivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, newStateProtoHashes,
- mNoReportMetricIds);
- mAllAtomMatchingTrackers = newAtomMatchingTrackers;
- mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
- mAllConditionTrackers = newConditionTrackers;
- mConditionTrackerMap = newConditionTrackerMap;
- mAllMetricProducers = newMetricProducers;
- mMetricProducerMap = newMetricProducerMap;
- mStateProtoHashes = newStateProtoHashes;
- mAllAnomalyTrackers = newAnomalyTrackers;
- mAlertTrackerMap = newAlertTrackerMap;
- mAllPeriodicAlarmTrackers = newPeriodicAlarmTrackers;
-
- mTtlNs = config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1;
- refreshTtl(currentTimeNs);
-
- mHashStringsInReport = config.hash_strings_in_metric_report();
- mVersionStringsInReport = config.version_strings_in_metric_report();
- mInstallerInReport = config.installer_in_metric_report();
- mWhitelistedAtomIds.clear();
- mWhitelistedAtomIds.insert(config.whitelisted_atom_ids().begin(),
- config.whitelisted_atom_ids().end());
- mShouldPersistHistory = config.persist_locally();
-
- // Store the sub-configs used.
- mAnnotations.clear();
- for (const auto& annotation : config.annotation()) {
- mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
- }
-
- mAllowedUid.clear();
- mAllowedPkg.clear();
- mDefaultPullUids.clear();
- mPullAtomUids.clear();
- mPullAtomPackages.clear();
- createAllLogSourcesFromConfig(config);
-
- verifyGuardrailsAndUpdateStatsdStats();
- initializeConfigActiveStatus();
- return mConfigValid;
-}
-
-void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) {
- // Init allowed pushed atom uids.
- if (config.allowed_log_source_size() == 0) {
- mConfigValid = false;
- ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at "
- "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
- } else {
- for (const auto& source : config.allowed_log_source()) {
- auto it = UidMap::sAidToUidMapping.find(source);
- if (it != UidMap::sAidToUidMapping.end()) {
- mAllowedUid.push_back(it->second);
- } else {
- mAllowedPkg.push_back(source);
- }
- }
-
- if (mAllowedUid.size() + mAllowedPkg.size() > StatsdStats::kMaxLogSourceCount) {
- ALOGE("Too many log sources. This is likely to be an error in the config.");
- mConfigValid = false;
- } else {
- initAllowedLogSources();
- }
- }
-
- // Init default allowed pull atom uids.
- int numPullPackages = 0;
- for (const string& pullSource : config.default_pull_packages()) {
- auto it = UidMap::sAidToUidMapping.find(pullSource);
- if (it != UidMap::sAidToUidMapping.end()) {
- numPullPackages++;
- mDefaultPullUids.insert(it->second);
- } else {
- ALOGE("Default pull atom packages must be in sAidToUidMapping");
- mConfigValid = false;
- }
- }
- // Init per-atom pull atom packages.
- for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) {
- int32_t atomId = pullAtomPackages.atom_id();
- for (const string& pullPackage : pullAtomPackages.packages()) {
- numPullPackages++;
- auto it = UidMap::sAidToUidMapping.find(pullPackage);
- if (it != UidMap::sAidToUidMapping.end()) {
- mPullAtomUids[atomId].insert(it->second);
- } else {
- mPullAtomPackages[atomId].insert(pullPackage);
- }
- }
- }
- if (numPullPackages > StatsdStats::kMaxPullAtomPackages) {
- ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to "
- "be an error in the config");
- mConfigValid = false;
- } else {
- initPullAtomSources();
- }
-}
-
-void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() {
- // Guardrail. Reject the config if it's too big.
- if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
- mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
- mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
- ALOGE("This config is too big! Reject!");
- mConfigValid = false;
- }
- if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) {
- ALOGE("This config has too many alerts! Reject!");
- mConfigValid = false;
- }
- // no matter whether this config is valid, log it in the stats.
- StatsdStats::getInstance().noteConfigReceived(
- mConfigKey, mAllMetricProducers.size(), mAllConditionTrackers.size(),
- mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations,
- mConfigValid);
-}
-
-void MetricsManager::initializeConfigActiveStatus() {
- mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
- (mAllMetricProducers.size() == 0);
- mIsActive = mIsAlwaysActive;
- for (int metric : mMetricIndexesWithActivation) {
- mIsActive |= mAllMetricProducers[metric]->isActive();
- }
- VLOG("mIsActive is initialized to %d", mIsActive);
-}
-
-void MetricsManager::initAllowedLogSources() {
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- mAllowedLogSources.clear();
- mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
-
- for (const auto& pkg : mAllowedPkg) {
- auto uids = mUidMap->getAppUid(pkg);
- mAllowedLogSources.insert(uids.begin(), uids.end());
- }
- if (DEBUG) {
- for (const auto& uid : mAllowedLogSources) {
- VLOG("Allowed uid %d", uid);
- }
- }
-}
-
-void MetricsManager::initPullAtomSources() {
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- mCombinedPullAtomUids.clear();
- for (const auto& [atomId, uids] : mPullAtomUids) {
- mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
- }
- for (const auto& [atomId, packages] : mPullAtomPackages) {
- for (const string& pkg : packages) {
- set<int32_t> uids = mUidMap->getAppUid(pkg);
- mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
- }
- }
-}
-
-bool MetricsManager::isConfigValid() const {
- return mConfigValid;
-}
-
-void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) {
- // Inform all metric producers.
- for (const auto& it : mAllMetricProducers) {
- it->notifyAppUpgrade(eventTimeNs);
- }
- // check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
- // We will re-initialize the whole list because we don't want to keep the multi mapping of
- // UID<->pkg inside MetricsManager to reduce the memory usage.
- initAllowedLogSources();
- }
-
- for (const auto& it : mPullAtomPackages) {
- if (it.second.find(apk) != it.second.end()) {
- initPullAtomSources();
- return;
- }
- }
-}
-
-void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
- const int uid) {
- // Inform all metric producers.
- for (const auto& it : mAllMetricProducers) {
- it->notifyAppRemoved(eventTimeNs);
- }
- // check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
- // We will re-initialize the whole list because we don't want to keep the multi mapping of
- // UID<->pkg inside MetricsManager to reduce the memory usage.
- initAllowedLogSources();
- }
-
- for (const auto& it : mPullAtomPackages) {
- if (it.second.find(apk) != it.second.end()) {
- initPullAtomSources();
- return;
- }
- }
-}
-
-void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
- // Purposefully don't inform metric producers on a new snapshot
- // because we don't need to flush partial buckets.
- // This occurs if a new user is added/removed or statsd crashes.
- initPullAtomSources();
-
- if (mAllowedPkg.size() == 0) {
- return;
- }
- initAllowedLogSources();
-}
-
-void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
- // Inform all metric producers.
- for (const auto& it : mAllMetricProducers) {
- it->onStatsdInitCompleted(eventTimeNs);
- }
-}
-
-void MetricsManager::init() {
- for (const auto& producer : mAllMetricProducers) {
- producer->prepareFirstBucket();
- }
-}
-
-vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) {
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- vector<int32_t> uids;
- const auto& it = mCombinedPullAtomUids.find(atomId);
- if (it != mCombinedPullAtomUids.end()) {
- uids.insert(uids.end(), it->second.begin(), it->second.end());
- }
- uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end());
- return uids;
-}
-
-void MetricsManager::dumpStates(FILE* out, bool verbose) {
- fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
- {
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- for (const auto& source : mAllowedLogSources) {
- fprintf(out, "%d ", source);
- }
- }
- fprintf(out, "\n");
- for (const auto& producer : mAllMetricProducers) {
- producer->dumpStates(out, verbose);
- }
-}
-
-void MetricsManager::dropData(const int64_t dropTimeNs) {
- for (const auto& producer : mAllMetricProducers) {
- producer->dropData(dropTimeNs);
- }
-}
-
-void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- VLOG("=========================Metric Reports Start==========================");
- // one StatsLogReport per MetricProduer
- for (const auto& producer : mAllMetricProducers) {
- if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
- uint64_t token = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
- if (mHashStringsInReport) {
- producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
- dumpLatency, str_set, protoOutput);
- } else {
- producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
- dumpLatency, nullptr, protoOutput);
- }
- protoOutput->end(token);
- } else {
- producer->clearPastBuckets(dumpTimeStampNs);
- }
- }
- for (const auto& annotation : mAnnotations) {
- uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ANNOTATIONS);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ANNOTATIONS_INT64,
- (long long)annotation.first);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ANNOTATIONS_INT32, annotation.second);
- protoOutput->end(token);
- }
-
- // Do not update the timestamps when data is not cleared to avoid timestamps from being
- // misaligned.
- if (erase_data) {
- mLastReportTimeNs = dumpTimeStampNs;
- mLastReportWallClockNs = getWallClockNs();
- }
- VLOG("=========================Metric Reports End==========================");
-}
-
-
-bool MetricsManager::checkLogCredentials(const LogEvent& event) {
- if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
- return true;
- }
- std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
- if (mAllowedLogSources.find(event.GetUid()) == mAllowedLogSources.end()) {
- VLOG("log source %d not on the whitelist", event.GetUid());
- return false;
- }
- return true;
-}
-
-bool MetricsManager::eventSanityCheck(const LogEvent& event) {
- if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) {
- // Check that app breadcrumb reported fields are valid.
- status_t err = NO_ERROR;
-
- // Uid is 3rd from last field and must match the caller's uid,
- // unless that caller is statsd itself (statsd is allowed to spoof uids).
- long appHookUid = event.GetLong(event.size()-2, &err);
- if (err != NO_ERROR) {
- VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
- return false;
- }
-
- // Because the uid within the LogEvent may have been mapped from
- // isolated to host, map the loggerUid similarly before comparing.
- int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid());
- if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
- VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
- appHookUid, loggerUid);
- return false;
- }
-
- // The state must be from 0,3. This part of code must be manually updated.
- long appHookState = event.GetLong(event.size(), &err);
- if (err != NO_ERROR) {
- VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
- return false;
- } else if (appHookState < 0 || appHookState > 3) {
- VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
- return false;
- }
- } else if (event.GetTagId() == util::DAVEY_OCCURRED) {
- // Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
- // Check that the davey duration is reasonable. Max length check is for privacy.
- status_t err = NO_ERROR;
-
- // Uid is the first field provided.
- long jankUid = event.GetLong(1, &err);
- if (err != NO_ERROR) {
- VLOG("Davey occurred had error when parsing the uid");
- return false;
- }
- int32_t loggerUid = event.GetUid();
- if (loggerUid != jankUid && loggerUid != AID_STATSD) {
- VLOG("DAVEY_OCCURRED has invalid uid: claimed %ld but caller is %d", jankUid,
- loggerUid);
- return false;
- }
-
- long duration = event.GetLong(event.size(), &err);
- if (err != NO_ERROR) {
- VLOG("Davey occurred had error when parsing the duration");
- return false;
- } else if (duration > 100000) {
- VLOG("Davey duration is unreasonably long: %ld", duration);
- return false;
- }
- }
-
- return true;
-}
-
-// Consume the stats log if it's interesting to this metric.
-void MetricsManager::onLogEvent(const LogEvent& event) {
- if (!mConfigValid) {
- return;
- }
-
- if (!checkLogCredentials(event)) {
- return;
- }
-
- if (!eventSanityCheck(event)) {
- return;
- }
-
- int tagId = event.GetTagId();
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
-
- bool isActive = mIsAlwaysActive;
-
- // Set of metrics that are still active after flushing.
- unordered_set<int> activeMetricsIndices;
-
- // Update state of all metrics w/ activation conditions as of eventTimeNs.
- for (int metricIndex : mMetricIndexesWithActivation) {
- const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
- metric->flushIfExpire(eventTimeNs);
- if (metric->isActive()) {
- // If this metric w/ activation condition is still active after
- // flushing, remember it.
- activeMetricsIndices.insert(metricIndex);
- }
- }
-
- mIsActive = isActive || !activeMetricsIndices.empty();
-
- if (mTagIds.find(tagId) == mTagIds.end()) {
- // Not interesting...
- return;
- }
-
- vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(),
- MatchingState::kNotComputed);
-
- // Evaluate all atom matchers.
- for (auto& matcher : mAllAtomMatchingTrackers) {
- matcher->onLogEvent(event, mAllAtomMatchingTrackers, matcherCache);
- }
-
- // Set of metrics that received an activation cancellation.
- unordered_set<int> metricIndicesWithCanceledActivations;
-
- // Determine which metric activations received a cancellation and cancel them.
- for (const auto& it : mDeactivationAtomTrackerToMetricMap) {
- if (matcherCache[it.first] == MatchingState::kMatched) {
- for (int metricIndex : it.second) {
- mAllMetricProducers[metricIndex]->cancelEventActivation(it.first);
- metricIndicesWithCanceledActivations.insert(metricIndex);
- }
- }
- }
-
- // Determine whether any metrics are no longer active after cancelling metric activations.
- for (const int metricIndex : metricIndicesWithCanceledActivations) {
- const sp<MetricProducer>& metric = mAllMetricProducers[metricIndex];
- metric->flushIfExpire(eventTimeNs);
- if (!metric->isActive()) {
- activeMetricsIndices.erase(metricIndex);
- }
- }
-
- isActive |= !activeMetricsIndices.empty();
-
-
- // Determine which metric activations should be turned on and turn them on
- for (const auto& it : mActivationAtomTrackerToMetricMap) {
- if (matcherCache[it.first] == MatchingState::kMatched) {
- for (int metricIndex : it.second) {
- mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs);
- isActive |= mAllMetricProducers[metricIndex]->isActive();
- }
- }
- }
-
- mIsActive = isActive;
-
- // A bitmap to see which ConditionTracker needs to be re-evaluated.
- vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
-
- for (const auto& pair : mTrackerToConditionMap) {
- if (matcherCache[pair.first] == MatchingState::kMatched) {
- const auto& conditionList = pair.second;
- for (const int conditionIndex : conditionList) {
- conditionToBeEvaluated[conditionIndex] = true;
- }
- }
- }
-
- vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
- ConditionState::kNotEvaluated);
- // A bitmap to track if a condition has changed value.
- vector<bool> changedCache(mAllConditionTrackers.size(), false);
- for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
- if (conditionToBeEvaluated[i] == false) {
- continue;
- }
- sp<ConditionTracker>& condition = mAllConditionTrackers[i];
- condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
- changedCache);
- }
-
- for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
- if (changedCache[i] == false) {
- continue;
- }
- auto pair = mConditionToMetricMap.find(i);
- if (pair != mConditionToMetricMap.end()) {
- auto& metricList = pair->second;
- for (auto metricIndex : metricList) {
- // Metric cares about non sliced condition, and it's changed.
- // Push the new condition to it directly.
- if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
- mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
- eventTimeNs);
- // Metric cares about sliced conditions, and it may have changed. Send
- // notification, and the metric can query the sliced conditions that are
- // interesting to it.
- } else {
- mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
- eventTimeNs);
- }
- }
- }
- }
-
- // For matched AtomMatchers, tell relevant metrics that a matched event has come.
- for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) {
- if (matcherCache[i] == MatchingState::kMatched) {
- StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
- mAllAtomMatchingTrackers[i]->getId());
- auto pair = mTrackerToMetricMap.find(i);
- if (pair != mTrackerToMetricMap.end()) {
- auto& metricList = pair->second;
- for (const int metricIndex : metricList) {
- // pushed metrics are never scheduled pulls
- mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
- }
- }
- }
- }
-}
-
-void MetricsManager::onAnomalyAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
- for (const auto& itr : mAllAnomalyTrackers) {
- itr->informAlarmsFired(timestampNs, alarmSet);
- }
-}
-
-void MetricsManager::onPeriodicAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
- for (const auto& itr : mAllPeriodicAlarmTrackers) {
- itr->informAlarmsFired(timestampNs, alarmSet);
- }
-}
-
-// Returns the total byte size of all metrics managed by a single config source.
-size_t MetricsManager::byteSize() {
- size_t totalSize = 0;
- for (const auto& metricProducer : mAllMetricProducers) {
- totalSize += metricProducer->byteSize();
- }
- return totalSize;
-}
-
-void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) {
- if (config.metric_size() == 0) {
- ALOGW("No active metric for config %s", mConfigKey.ToString().c_str());
- return;
- }
-
- for (int i = 0; i < config.metric_size(); i++) {
- const auto& activeMetric = config.metric(i);
- for (int metricIndex : mMetricIndexesWithActivation) {
- const auto& metric = mAllMetricProducers[metricIndex];
- if (metric->getMetricId() == activeMetric.id()) {
- VLOG("Setting active metric: %lld", (long long)metric->getMetricId());
- metric->loadActiveMetric(activeMetric, currentTimeNs);
- if (!mIsActive && metric->isActive()) {
- StatsdStats::getInstance().noteActiveStatusChanged(mConfigKey,
- /*activate=*/ true);
- }
- mIsActive |= metric->isActive();
- }
- }
- }
-}
-
-void MetricsManager::writeActiveConfigToProtoOutputStream(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId());
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid());
- for (int metricIndex : mMetricIndexesWithActivation) {
- const auto& metric = mAllMetricProducers[metricIndex];
- const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_ACTIVE_CONFIG_METRIC);
- metric->writeActiveMetricToProtoOutputStream(currentTimeNs, reason, proto);
- proto->end(metricToken);
- }
-}
-
-bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::StatsMetadata* statsMetadata) {
- bool metadataWritten = false;
- metadata::ConfigKey* configKey = statsMetadata->mutable_config_key();
- configKey->set_config_id(mConfigKey.GetId());
- configKey->set_uid(mConfigKey.GetUid());
- for (const auto& anomalyTracker : mAllAnomalyTrackers) {
- metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata();
- bool alertWritten = anomalyTracker->writeAlertMetadataToProto(currentWallClockTimeNs,
- systemElapsedTimeNs, alertMetadata);
- if (!alertWritten) {
- statsMetadata->mutable_alert_metadata()->RemoveLast();
- }
- metadataWritten |= alertWritten;
- }
- return metadataWritten;
-}
-
-void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs) {
- for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) {
- int64_t alertId = alertMetadata.alert_id();
- auto it = mAlertTrackerMap.find(alertId);
- if (it == mAlertTrackerMap.end()) {
- ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId);
- continue;
- }
- mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata,
- currentWallClockTimeNs,
- systemElapsedTimeNs);
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
deleted file mode 100644
index 3c9ecdb46fd2..000000000000
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ /dev/null
@@ -1,383 +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.
- */
-
-#pragma once
-
-#include "anomaly/AlarmMonitor.h"
-#include "anomaly/AlarmTracker.h"
-#include "anomaly/AnomalyTracker.h"
-#include "condition/ConditionTracker.h"
-#include "config/ConfigKey.h"
-#include "external/StatsPullerManager.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
-#include "logd/LogEvent.h"
-#include "matchers/AtomMatchingTracker.h"
-#include "metrics/MetricProducer.h"
-#include "packages/UidMap.h"
-
-#include <unordered_map>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// A MetricsManager is responsible for managing metrics from one single config source.
-class MetricsManager : public virtual android::RefBase, public virtual PullUidProvider {
-public:
- MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<UidMap>& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor);
-
- virtual ~MetricsManager();
-
- bool updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor);
-
- // Return whether the configuration is valid.
- bool isConfigValid() const;
-
- bool checkLogCredentials(const LogEvent& event);
-
- bool eventSanityCheck(const LogEvent& event);
-
- void onLogEvent(const LogEvent& event);
-
- void onAnomalyAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
-
- void onPeriodicAlarmFired(
- const int64_t& timestampNs,
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
-
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version);
-
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid);
-
- void onUidMapReceived(const int64_t& eventTimeNs);
-
- void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
-
- void init();
-
- vector<int32_t> getPullAtomUids(int32_t atomId) override;
-
- bool shouldWriteToDisk() const {
- return mNoReportMetricIds.size() != mAllMetricProducers.size();
- }
-
- bool shouldPersistLocalHistory() const {
- return mShouldPersistHistory;
- }
-
- void dumpStates(FILE* out, bool verbose);
-
- inline bool isInTtl(const int64_t timestampNs) const {
- return mTtlNs <= 0 || timestampNs < mTtlEndNs;
- };
-
- inline bool hashStringInReport() const {
- return mHashStringsInReport;
- };
-
- inline bool versionStringsInReport() const {
- return mVersionStringsInReport;
- };
-
- inline bool installerInReport() const {
- return mInstallerInReport;
- };
-
- void refreshTtl(const int64_t currentTimestampNs) {
- if (mTtlNs > 0) {
- mTtlEndNs = currentTimestampNs + mTtlNs;
- }
- };
-
- // Returns the elapsed realtime when this metric manager last reported metrics. If this config
- // has not yet dumped any reports, this is the time the metricsmanager was initialized.
- inline int64_t getLastReportTimeNs() const {
- return mLastReportTimeNs;
- };
-
- inline int64_t getLastReportWallClockNs() const {
- return mLastReportWallClockNs;
- };
-
- inline size_t getNumMetrics() const {
- return mAllMetricProducers.size();
- }
-
- virtual void dropData(const int64_t dropTimeNs);
-
- virtual void onDumpReport(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput);
-
- // Computes the total byte size of all metrics managed by a single config source.
- // Does not change the state.
- virtual size_t byteSize();
-
- // Returns whether or not this config is active.
- // The config is active if any metric in the config is active.
- inline bool isActive() const {
- return mIsActive;
- }
-
- void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs);
-
- void writeActiveConfigToProtoOutputStream(
- int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
-
- // Returns true if at least one piece of metadata is written.
- bool writeMetadataToProto(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::StatsMetadata* statsMetadata);
-
- void loadMetadata(const metadata::StatsMetadata& metadata,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs);
-private:
- // For test only.
- inline int64_t getTtlEndNs() const { return mTtlEndNs; }
-
- const ConfigKey mConfigKey;
-
- sp<UidMap> mUidMap;
-
- bool mConfigValid = false;
-
- bool mHashStringsInReport = false;
- bool mVersionStringsInReport = false;
- bool mInstallerInReport = false;
-
- int64_t mTtlNs;
- int64_t mTtlEndNs;
-
- int64_t mLastReportTimeNs;
- int64_t mLastReportWallClockNs;
-
- sp<StatsPullerManager> mPullerManager;
-
- // The uid log sources from StatsdConfig.
- std::vector<int32_t> mAllowedUid;
-
- // The pkg log sources from StatsdConfig.
- std::vector<std::string> mAllowedPkg;
-
- // The combined uid sources (after translating pkg name to uid).
- // Logs from uids that are not in the list will be ignored to avoid spamming.
- std::set<int32_t> mAllowedLogSources;
-
- // To guard access to mAllowedLogSources
- mutable std::mutex mAllowedLogSourcesMutex;
-
- std::set<int32_t> mWhitelistedAtomIds;
-
- // We can pull any atom from these uids.
- std::set<int32_t> mDefaultPullUids;
-
- // Uids that specific atoms can pull from.
- // This is a map<atom id, set<uids>>
- std::map<int32_t, std::set<int32_t>> mPullAtomUids;
-
- // Packages that specific atoms can be pulled from.
- std::map<int32_t, std::set<std::string>> mPullAtomPackages;
-
- // All uids to pull for this atom. NOTE: Does not include the default uids for memory.
- std::map<int32_t, std::set<int32_t>> mCombinedPullAtomUids;
-
- // Contains the annotations passed in with StatsdConfig.
- std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
-
- bool mShouldPersistHistory;
-
- // All event tags that are interesting to my metrics.
- std::set<int> mTagIds;
-
- // We only store the sp of AtomMatchingTracker, MetricProducer, and ConditionTracker in
- // MetricsManager. There are relationships between them, and the relationships are denoted by
- // index instead of pointers. The reasons for this are: (1) the relationship between them are
- // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B
- // holds A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get
- // the related results from a cache using the index.
-
- // Hold all the atom matchers from the config.
- std::vector<sp<AtomMatchingTracker>> mAllAtomMatchingTrackers;
-
- // Hold all the conditions from the config.
- std::vector<sp<ConditionTracker>> mAllConditionTrackers;
-
- // Hold all metrics from the config.
- std::vector<sp<MetricProducer>> mAllMetricProducers;
-
- // Hold all alert trackers.
- std::vector<sp<AnomalyTracker>> mAllAnomalyTrackers;
-
- // Hold all periodic alarm trackers.
- std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers;
-
- // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and
- // ConditionTracker to its index in the corresponding vector.
-
- // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers.
- std::unordered_map<int64_t, int> mAtomMatchingTrackerMap;
-
- // Maps the id of a condition tracker to its index in mAllConditionTrackers.
- std::unordered_map<int64_t, int> mConditionTrackerMap;
-
- // Maps the id of a metric producer to its index in mAllMetricProducers.
- std::unordered_map<int64_t, int> mMetricProducerMap;
-
- // To make the log processing more efficient, we want to do as much filtering as possible
- // before we go into individual trackers and conditions to match.
-
- // 1st filter: check if the event tag id is in mTagIds.
- // 2nd filter: if it is, we parse the event because there is at least one member is interested.
- // then pass to all AtomMatchingTrackers (itself also filter events by ids).
- // 3nd filter: for AtomMatchingTrackers that matched this event, we pass this event to the
- // ConditionTrackers and MetricProducers that use this matcher.
- // 4th filter: for ConditionTrackers that changed value due to this event, we pass
- // new conditions to metrics that use this condition.
-
- // The following map is initialized from the statsd_config.
-
- // Maps from the index of the AtomMatchingTracker to index of MetricProducer.
- std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
-
- // Maps from AtomMatchingTracker to ConditionTracker
- std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
-
- // Maps from ConditionTracker to MetricProducer
- std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
-
- // Maps from life span triggering event to MetricProducers.
- std::unordered_map<int, std::vector<int>> mActivationAtomTrackerToMetricMap;
-
- // Maps deactivation triggering event to MetricProducers.
- std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap;
-
- // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers.
- // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId.
- std::unordered_map<int64_t, int> mAlertTrackerMap;
-
- std::vector<int> mMetricIndexesWithActivation;
-
- void initAllowedLogSources();
-
- void initPullAtomSources();
-
- // Only called on config creation/update to initialize log sources from the config.
- // Calls initAllowedLogSources and initPullAtomSources. Sets mConfigValid to false on error.
- void createAllLogSourcesFromConfig(const StatsdConfig& config);
-
- // Verifies the config meets guardrails and updates statsdStats.
- // Sets mConfigValid to false on error. Should be called on config creation/update
- void verifyGuardrailsAndUpdateStatsdStats();
-
- // Initializes mIsAlwaysActive and mIsActive.
- // Should be called on config creation/update.
- void initializeConfigActiveStatus();
-
- // The metrics that don't need to be uploaded or even reported.
- std::set<int64_t> mNoReportMetricIds;
-
- // The config is active if any metric in the config is active.
- bool mIsActive;
-
- // The config is always active if any metric in the config does not have an activation signal.
- bool mIsAlwaysActive;
-
- // Hashes of the States used in this config, keyed by the state id, used in config updates.
- std::map<int64_t, uint64_t> mStateProtoHashes;
-
- FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
- FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
- FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
- FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
- FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
- FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
- FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
-
- FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
- FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
-
- FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
- FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
- FRIEND_TEST(ConfigUpdateE2eTest, TestConfigTtl);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
- FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
-
- FRIEND_TEST(MetricsManagerTest, TestLogSources);
- FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate);
-
- FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
- FRIEND_TEST(StatsLogProcessorTest,
- TestActivationOnBootMultipleActivationsDifferentActivationTypes);
- FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
-
- FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
- FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
- FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
-
- FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
- FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
- FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
- FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
- FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset);
- FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
-
- FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
- FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
deleted file mode 100644
index 22fdf1604435..000000000000
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ /dev/null
@@ -1,1282 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "ValueMetricProducer.h"
-
-#include <limits.h>
-#include <stdlib.h>
-
-#include "../guardrail/StatsdStats.h"
-#include "../stats_log_util.h"
-#include "metrics/parsing_utils/metrics_manager_util.h"
-
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_DOUBLE;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-using std::map;
-using std::shared_ptr;
-using std::unordered_map;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for StatsLogReport
-const int FIELD_ID_ID = 1;
-const int FIELD_ID_VALUE_METRICS = 7;
-const int FIELD_ID_TIME_BASE = 9;
-const int FIELD_ID_BUCKET_SIZE = 10;
-const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_IS_ACTIVE = 14;
-// for ValueMetricDataWrapper
-const int FIELD_ID_DATA = 1;
-const int FIELD_ID_SKIPPED = 2;
-// for SkippedBuckets
-const int FIELD_ID_SKIPPED_START_MILLIS = 3;
-const int FIELD_ID_SKIPPED_END_MILLIS = 4;
-const int FIELD_ID_SKIPPED_DROP_EVENT = 5;
-// for DumpEvent Proto
-const int FIELD_ID_BUCKET_DROP_REASON = 1;
-const int FIELD_ID_DROP_TIME = 2;
-// for ValueMetricData
-const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_BUCKET_INFO = 3;
-const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_SLICE_BY_STATE = 6;
-// for ValueBucketInfo
-const int FIELD_ID_VALUE_INDEX = 1;
-const int FIELD_ID_VALUE_LONG = 2;
-const int FIELD_ID_VALUE_DOUBLE = 3;
-const int FIELD_ID_VALUES = 9;
-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;
-const int FIELD_ID_CONDITION_TRUE_NS = 10;
-
-const Value ZERO_LONG((int64_t)0);
-const Value ZERO_DOUBLE((int64_t)0);
-
-// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
-ValueMetricProducer::ValueMetricProducer(
- const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
- const int whatMatcherIndex, 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 vector<int>& slicedStateAtoms,
- const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache,
- conditionWizard, protoHash, eventActivationMap, eventDeactivationMap,
- slicedStateAtoms, stateGroupMap),
- mWhatMatcherIndex(whatMatcherIndex),
- mEventMatcherWizard(matcherWizard),
- mPullerManager(pullerManager),
- mPullTagId(pullTagId),
- mIsPulled(pullTagId != -1),
- mMinBucketSizeNs(metric.min_bucket_size_nanos()),
- mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
- StatsdStats::kAtomDimensionKeySizeLimitMap.end()
- ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).first
- : StatsdStats::kDimensionKeySizeSoftLimit),
- mDimensionHardLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
- StatsdStats::kAtomDimensionKeySizeLimitMap.end()
- ? StatsdStats::kAtomDimensionKeySizeLimitMap.at(pullTagId).second
- : StatsdStats::kDimensionKeySizeHardLimit),
- mUseAbsoluteValueOnReset(metric.use_absolute_value_on_reset()),
- mAggregationType(metric.aggregation_type()),
- mUseDiff(metric.has_use_diff() ? metric.use_diff() : (mIsPulled ? true : false)),
- mValueDirection(metric.value_direction()),
- mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
- mUseZeroDefaultBase(metric.use_zero_default_base()),
- mHasGlobalBase(false),
- mCurrentBucketIsSkipped(false),
- mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
- : StatsdStats::kPullMaxDelayNs),
- mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
- // Condition timer will be set later within the constructor after pulling events
- mConditionTimer(false, timeBaseNs) {
- int64_t bucketSizeMills = 0;
- if (metric.has_bucket()) {
- bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
- } else {
- bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
- }
-
- mBucketSizeNs = bucketSizeMills * 1000000;
-
- translateFieldMatcher(metric.value_field(), &mFieldMatchers);
-
- if (metric.has_dimensions_in_what()) {
- translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
- mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
- }
-
- if (metric.links().size() > 0) {
- for (const auto& link : metric.links()) {
- Metric2Condition mc;
- mc.conditionId = link.condition();
- translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
- translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
- mMetric2ConditionLinks.push_back(mc);
- }
- mConditionSliced = true;
- }
-
- for (const auto& stateLink : metric.state_link()) {
- Metric2State ms;
- ms.stateAtomId = stateLink.state_atom_id();
- translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
- translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
- mMetric2StateLinks.push_back(ms);
- }
-
- int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs);
- mCurrentBucketNum += numBucketsForward;
-
- flushIfNeededLocked(startTimeNs);
-
- if (mIsPulled) {
- mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
- mBucketSizeNs);
- }
-
- // Only do this for partial buckets like first bucket. All other buckets should use
- // flushIfNeeded to adjust start and end to bucket boundaries.
- // Adjust start for partial bucket
- mCurrentBucketStartTimeNs = startTimeNs;
- mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
-
- // Now that activations are processed, start the condition timer if needed.
- mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
- mCurrentBucketStartTimeNs);
-
- VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
- (long long)mBucketSizeNs, (long long)mTimeBaseNs);
-}
-
-ValueMetricProducer::~ValueMetricProducer() {
- VLOG("~ValueMetricProducer() called");
- if (mIsPulled) {
- mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
- }
-}
-
-bool ValueMetricProducer::onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!MetricProducer::onConfigUpdatedLocked(
- config, configIndex, metricIndex, allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
- trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- return false;
- }
-
- const ValueMetric& metric = config.value_metric(configIndex);
- // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap,
- trackerToMetricMap, mWhatMatcherIndex)) {
- return false;
- }
-
- if (metric.has_condition() &&
- !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, mConditionTrackerIndex,
- conditionToMetricMap)) {
- return false;
- }
- sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
- mEventMatcherWizard = matcherWizard;
- return true;
-}
-
-void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
- const HashableDimensionKey& primaryKey,
- const FieldValue& oldState, const FieldValue& newState) {
- VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d",
- (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
- oldState.mValue.int_value, newState.mValue.int_value);
-
- // If old and new states are in the same StateGroup, then we do not need to
- // pull for this state change.
- FieldValue oldStateCopy = oldState;
- FieldValue newStateCopy = newState;
- mapStateValue(atomId, &oldStateCopy);
- mapStateValue(atomId, &newStateCopy);
- if (oldStateCopy == newStateCopy) {
- return;
- }
-
- // If condition is not true or metric is not active, we do not need to pull
- // for this state change.
- if (mCondition != ConditionState::kTrue || !mIsActive) {
- return;
- }
-
- bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs;
- if (isEventLate) {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
- (long long)mCurrentBucketStartTimeNs);
- invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
- return;
- }
- mStateChangePrimaryKey.first = atomId;
- mStateChangePrimaryKey.second = primaryKey;
- if (mIsPulled) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
- mStateChangePrimaryKey.first = 0;
- mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY;
- flushIfNeededLocked(eventTimeNs);
-}
-
-void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
-}
-
-void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
- StatsdStats::getInstance().noteBucketDropped(mMetricId);
-
- // The current partial bucket is not flushed and does not require a pull,
- // so the data is still valid.
- flushIfNeededLocked(dropTimeNs);
- clearPastBucketsLocked(dropTimeNs);
-}
-
-void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) {
- mPastBuckets.clear();
- mSkippedBuckets.clear();
-}
-
-void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- VLOG("metric %lld dump report now...", (long long)mMetricId);
- if (include_current_partial_bucket) {
- // For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the
- // current bucket will have incomplete data and the next will have the wrong snapshot to do
- // a diff against. If the condition is false, we are fine since the base data is reset and
- // we are not tracking anything.
- bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
- if (pullNeeded) {
- switch (dumpLatency) {
- case FAST:
- invalidateCurrentBucket(dumpTimeNs, BucketDropReason::DUMP_REPORT_REQUESTED);
- break;
- case NO_TIME_CONSTRAINTS:
- pullAndMatchEventsLocked(dumpTimeNs);
- break;
- }
- }
- flushCurrentBucketLocked(dumpTimeNs, dumpTimeNs);
- }
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
-
- if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
- return;
- }
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TIME_BASE, (long long)mTimeBaseNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_SIZE, (long long)mBucketSizeNs);
- // Fills the dimension path if not slicing by ALL.
- if (!mSliceByPositionALL) {
- if (!mDimensionsInWhat.empty()) {
- uint64_t dimenPathToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_WHAT);
- writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
- protoOutput->end(dimenPathToken);
- }
- }
-
- uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
-
- for (const auto& skippedBucket : mSkippedBuckets) {
- uint64_t wrapperToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
- (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs)));
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
- (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs)));
- for (const auto& dropEvent : skippedBucket.dropEvents) {
- uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SKIPPED_DROP_EVENT);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
- (long long)(NanoToMillis(dropEvent.dropTimeNs)));
- protoOutput->end(dropEventToken);
- }
- protoOutput->end(wrapperToken);
- }
-
- for (const auto& pair : mPastBuckets) {
- const MetricDimensionKey& dimensionKey = pair.first;
- VLOG(" dimension key %s", dimensionKey.toString().c_str());
- uint64_t wrapperToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
-
- // First fill dimension.
- if (mSliceByPositionALL) {
- uint64_t dimensionToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
- protoOutput->end(dimensionToken);
- } else {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
- FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- }
-
- // Then fill slice_by_state.
- for (auto state : dimensionKey.getStateValuesKey().getValues()) {
- uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SLICE_BY_STATE);
- writeStateToProto(state, protoOutput);
- protoOutput->end(stateToken);
- }
-
- // Then fill bucket_info (ValueBucketInfo).
- for (const auto& bucket : pair.second) {
- uint64_t bucketInfoToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
-
- if (bucket.mBucketEndNs - bucket.mBucketStartNs != mBucketSizeNs) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketStartNs));
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_ELAPSED_MILLIS,
- (long long)NanoToMillis(bucket.mBucketEndNs));
- } else {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
- (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
- }
- // We only write the condition timer value if the metric has a
- // condition and/or is sliced by state.
- // If the metric is sliced by state, the condition timer value is
- // also sliced by state to reflect time spent in that state.
- if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
- (long long)bucket.mConditionTrueNs);
- }
- for (int i = 0; i < (int)bucket.valueIndex.size(); i++) {
- int index = bucket.valueIndex[i];
- const Value& value = bucket.values[i];
- uint64_t valueToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_VALUES);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_INDEX,
- index);
- if (value.getType() == LONG) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG,
- (long long)value.long_value);
- VLOG("\t bucket [%lld - %lld] value %d: %lld", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, index, (long long)value.long_value);
- } else if (value.getType() == DOUBLE) {
- protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE,
- value.double_value);
- VLOG("\t bucket [%lld - %lld] value %d: %.2f", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, index, value.double_value);
- } else {
- VLOG("Wrong value type for ValueMetric output: %d", value.getType());
- }
- protoOutput->end(valueToken);
- }
- protoOutput->end(bucketInfoToken);
- }
- protoOutput->end(wrapperToken);
- }
- protoOutput->end(protoToken);
-
- VLOG("metric %lld dump report now...", (long long)mMetricId);
- if (erase_data) {
- mPastBuckets.clear();
- mSkippedBuckets.clear();
- }
-}
-
-void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs,
- const BucketDropReason reason) {
- if (!mCurrentBucketIsSkipped) {
- // Only report to StatsdStats once per invalid bucket.
- StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
- }
-
- skipCurrentBucket(dropTimeNs, reason);
-}
-
-void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs,
- const BucketDropReason reason) {
- invalidateCurrentBucketWithoutResetBase(dropTimeNs, reason);
- resetBase();
-}
-
-void ValueMetricProducer::skipCurrentBucket(const int64_t dropTimeNs,
- const BucketDropReason reason) {
- if (!maxDropEventsReached()) {
- mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason));
- }
- mCurrentBucketIsSkipped = true;
-}
-
-void ValueMetricProducer::resetBase() {
- for (auto& slice : mCurrentBaseInfo) {
- for (auto& baseInfo : slice.second.baseInfos) {
- baseInfo.hasBase = false;
- }
- }
- mHasGlobalBase = false;
-}
-
-// Handle active state change. Active state change is treated like a condition change:
-// - drop bucket if active state change event arrives too late
-// - if condition is true, pull data on active state changes
-// - ConditionTimer tracks changes based on AND of condition and active state.
-void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
- bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
- if (isEventTooLate) {
- // Drop bucket because event arrived too late, ie. we are missing data for this bucket.
- StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
- invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
- }
-
- // Call parent method once we've verified the validity of current bucket.
- MetricProducer::onActiveStateChangedLocked(eventTimeNs);
-
- if (ConditionState::kTrue != mCondition) {
- return;
- }
-
- // Pull on active state changes.
- if (!isEventTooLate) {
- if (mIsPulled) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
- // When active state changes from true to false, clear diff base but don't
- // reset other counters as we may accumulate more value in the bucket.
- if (mUseDiff && !mIsActive) {
- resetBase();
- }
- }
-
- flushIfNeededLocked(eventTimeNs);
-
- // Let condition timer know of new active state.
- mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
-
- updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs);
-}
-
-void ValueMetricProducer::onConditionChangedLocked(const bool condition,
- const int64_t eventTimeNs) {
- ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
- bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
-
- // If the config is not active, skip the event.
- if (!mIsActive) {
- mCondition = isEventTooLate ? ConditionState::kUnknown : newCondition;
- return;
- }
-
- // If the event arrived late, mark the bucket as invalid and skip the event.
- if (isEventTooLate) {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
- (long long)mCurrentBucketStartTimeNs);
- StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
- StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
- invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
- mCondition = ConditionState::kUnknown;
- mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
-
- updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
- return;
- }
-
- // If the previous condition was unknown, mark the bucket as invalid
- // because the bucket will contain partial data. For example, the condition
- // change might happen close to the end of the bucket and we might miss a
- // lot of data.
- //
- // We still want to pull to set the base.
- if (mCondition == ConditionState::kUnknown) {
- invalidateCurrentBucket(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
- }
-
- // Pull and match for the following condition change cases:
- // unknown/false -> true - condition changed
- // true -> false - condition changed
- // true -> true - old condition was true so we can flush the bucket at the
- // end if needed.
- //
- // We don’t need to pull for unknown -> false or false -> false.
- //
- // onConditionChangedLocked might happen on bucket boundaries if this is
- // called before #onDataPulled.
- if (mIsPulled &&
- (newCondition == ConditionState::kTrue || mCondition == ConditionState::kTrue)) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
-
- // For metrics that use diff, when condition changes from true to false,
- // clear diff base but don't reset other counts because we may accumulate
- // more value in the bucket.
- if (mUseDiff &&
- (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)) {
- resetBase();
- }
-
- // Update condition state after pulling.
- mCondition = newCondition;
-
- flushIfNeededLocked(eventTimeNs);
- mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
-
- updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
-}
-
-void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition,
- int64_t eventTimeNs) {
- if (mSlicedStateAtoms.empty()) {
- return;
- }
-
- // Utilize the current state key of each DimensionsInWhat key to determine
- // which condition timers to update.
- //
- // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`.
- bool inPulledData;
- for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) {
- // If the new condition is true, turn ON the condition timer only if
- // the DimensionInWhat key was present in the pulled data.
- inPulledData = dimensionInWhatInfo.hasCurrentState;
- mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey,
- dimensionInWhatInfo.currentState)]
- .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs);
- }
-}
-
-void ValueMetricProducer::prepareFirstBucketLocked() {
- // Kicks off the puller immediately if condition is true and diff based.
- if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
- }
-}
-
-void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
- vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
- ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
- invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED);
- return;
- }
-
- accumulateEvents(allData, timestampNs, timestampNs);
-}
-
-int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
- return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
-}
-
-// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely
-// to be delayed. Other events like condition changes or app upgrade which are not based on
-// AlarmManager might have arrived earlier and close the bucket.
-void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
- bool pullSuccess, int64_t originalPullTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mCondition == ConditionState::kTrue) {
- // If the pull failed, we won't be able to compute a diff.
- if (!pullSuccess) {
- invalidateCurrentBucket(originalPullTimeNs, BucketDropReason::PULL_FAILED);
- } else {
- bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
- if (isEventLate) {
- // If the event is late, we are in the middle of a bucket. Just
- // process the data without trying to snap the data to the nearest bucket.
- accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs);
- } else {
- // For scheduled pulled data, the effective event time is snap to the nearest
- // bucket end. In the case of waking up from a deep sleep state, we will
- // attribute to the previous bucket end. If the sleep was long but not very
- // long, we will be in the immediate next bucket. Previous bucket may get a
- // larger number as we pull at a later time than real bucket end.
- //
- // If the sleep was very long, we skip more than one bucket before sleep. In
- // this case, if the diff base will be cleared and this new data will serve as
- // new diff base.
- int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
- StatsdStats::getInstance().noteBucketBoundaryDelayNs(
- mMetricId, originalPullTimeNs - bucketEndTime);
- accumulateEvents(allData, originalPullTimeNs, bucketEndTime);
- }
- }
- }
-
- // We can probably flush the bucket. Since we used bucketEndTime when calling
- // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
- flushIfNeededLocked(originalPullTimeNs);
-}
-
-void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) {
- bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs;
- if (isEventLate) {
- VLOG("Skip bucket end pull due to late arrival: %lld vs %lld",
- (long long)eventElapsedTimeNs, (long long)mCurrentBucketStartTimeNs);
- StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
- invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
- return;
- }
-
- const int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
- const int64_t pullDelayNs = elapsedRealtimeNs - originalPullTimeNs;
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
- if (pullDelayNs > mMaxPullDelayNs) {
- ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
- (long long)mMaxPullDelayNs);
- StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- // We are missing one pull from the bucket which means we will not have a complete view of
- // what's going on.
- invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::PULL_DELAYED);
- return;
- }
-
- mMatchedMetricDimensionKeys.clear();
- for (const auto& data : allData) {
- LogEvent localCopy = data->makeCopy();
- if (mEventMatcherWizard->matchLogEvent(localCopy, mWhatMatcherIndex) ==
- MatchingState::kMatched) {
- localCopy.setElapsedTimestampNs(eventElapsedTimeNs);
- onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
- }
- }
- // If a key that is:
- // 1. Tracked in mCurrentSlicedBucket and
- // 2. A superset of the current mStateChangePrimaryKey
- // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
- // then we need to reset the base.
- for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
- const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat();
- bool presentInPulledData =
- mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
- if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
- auto it = mCurrentBaseInfo.find(whatKey);
- for (auto& baseInfo : it->second.baseInfos) {
- baseInfo.hasBase = false;
- }
- // Set to false when DimensionInWhat key is not present in a pull.
- // Used in onMatchedLogEventInternalLocked() to ensure the condition
- // timer is turned on the next pull when data is present.
- it->second.hasCurrentState = false;
- // Turn OFF condition timer for keys not present in pulled data.
- currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs);
- }
- }
- mMatchedMetricDimensionKeys.clear();
- mHasGlobalBase = true;
-
- // If we reach the guardrail, we might have dropped some data which means the bucket is
- // incomplete.
- //
- // The base also needs to be reset. If we do not have the full data, we might
- // incorrectly compute the diff when mUseZeroDefaultBase is true since an existing key
- // might be missing from mCurrentSlicedBucket.
- if (hasReachedGuardRailLimit()) {
- invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::DIMENSION_GUARDRAIL_REACHED);
- mCurrentSlicedBucket.clear();
- }
-}
-
-void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
- if (mCurrentSlicedBucket.size() == 0) {
- return;
- }
-
- fprintf(out, "ValueMetric %lld dimension size %lu\n", (long long)mMetricId,
- (unsigned long)mCurrentSlicedBucket.size());
- if (verbose) {
- for (const auto& it : mCurrentSlicedBucket) {
- for (const auto& interval : it.second.intervals) {
- fprintf(out, "\t(what)%s\t(states)%s (value)%s\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getStateValuesKey().toString().c_str(),
- interval.value.toString().c_str());
- }
- }
- }
-}
-
-bool ValueMetricProducer::hasReachedGuardRailLimit() const {
- return mCurrentSlicedBucket.size() >= mDimensionHardLimit;
-}
-
-bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
- // ===========GuardRail==============
- // 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
- return false;
- }
- if (mCurrentSlicedBucket.size() > mDimensionSoftLimit - 1) {
- size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (hasReachedGuardRailLimit()) {
- ALOGE("ValueMetric %lld dropping data for dimension key %s", (long long)mMetricId,
- newKey.toString().c_str());
- StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
- return true;
- }
- }
-
- return false;
-}
-
-bool ValueMetricProducer::hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey) {
- // ===========GuardRail==============
- // 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentFullBucket.find(newKey) != mCurrentFullBucket.end()) {
- return false;
- }
- if (mCurrentFullBucket.size() > mDimensionSoftLimit - 1) {
- size_t newTupleCount = mCurrentFullBucket.size() + 1;
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > mDimensionHardLimit) {
- ALOGE("ValueMetric %lld dropping data for full bucket dimension key %s",
- (long long)mMetricId,
- newKey.toString().c_str());
- return true;
- }
- }
-
- return false;
-}
-
-bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) {
- for (const FieldValue& value : event.getValues()) {
- if (value.mField.matches(matcher)) {
- switch (value.mValue.type) {
- case INT:
- ret.setLong(value.mValue.int_value);
- break;
- case LONG:
- ret.setLong(value.mValue.long_value);
- break;
- case FLOAT:
- ret.setDouble(value.mValue.float_value);
- break;
- case DOUBLE:
- ret.setDouble(value.mValue.double_value);
- break;
- default:
- return false;
- break;
- }
- return true;
- }
- }
- return false;
-}
-
-bool ValueMetricProducer::multipleBucketsSkipped(const int64_t numBucketsForward) {
- // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
- return numBucketsForward > 1 && (mIsPulled || mUseDiff);
-}
-
-void ValueMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const map<int, HashableDimensionKey>& statePrimaryKeys) {
- auto whatKey = eventKey.getDimensionKeyInWhat();
- auto stateKey = eventKey.getStateValuesKey();
-
- // Skip this event if a state changed occurred for a different primary key.
- auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first);
- // Check that both the atom id and the primary key are equal.
- if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) {
- VLOG("ValueMetric skip event with primary key %s because state change primary key "
- "is %s",
- it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str());
- return;
- }
-
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
- if (eventTimeNs < mCurrentBucketStartTimeNs) {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
- (long long)mCurrentBucketStartTimeNs);
- return;
- }
- mMatchedMetricDimensionKeys.insert(whatKey);
-
- if (!mIsPulled) {
- // We cannot flush without doing a pull first.
- flushIfNeededLocked(eventTimeNs);
- }
-
- // We should not accumulate the data for pushed metrics when the condition is false.
- bool shouldSkipForPushMetric = !mIsPulled && !condition;
- // For pulled metrics, there are two cases:
- // - to compute diffs, we need to process all the state changes
- // - for non-diffs metrics, we should ignore the data if the condition wasn't true. If we have a
- // state change from
- // + True -> True: we should process the data, it might be a bucket boundary
- // + True -> False: we als need to process the data.
- bool shouldSkipForPulledMetric = mIsPulled && !mUseDiff
- && mCondition != ConditionState::kTrue;
- if (shouldSkipForPushMetric || shouldSkipForPulledMetric) {
- VLOG("ValueMetric skip event because condition is false and we are not using diff (for "
- "pulled metric)");
- return;
- }
-
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
-
- const auto& returnVal =
- mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey()));
- DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second;
- const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState;
- vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
- if (baseInfos.size() < mFieldMatchers.size()) {
- VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
- baseInfos.resize(mFieldMatchers.size());
- }
-
- // Ensure we turn on the condition timer in the case where dimensions
- // were missing on a previous pull due to a state change.
- bool stateChange = oldStateKey != stateKey;
- if (!dimensionsInWhatInfo.hasCurrentState) {
- stateChange = true;
- dimensionsInWhatInfo.hasCurrentState = true;
- }
-
- // We need to get the intervals stored with the previous state key so we can
- // close these value intervals.
- vector<Interval>& intervals =
- mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
- if (intervals.size() < mFieldMatchers.size()) {
- VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
- intervals.resize(mFieldMatchers.size());
- }
-
- // We only use anomaly detection under certain cases.
- // N.B.: The anomaly detection cases were modified in order to fix an issue with value metrics
- // containing multiple values. We tried to retain all previous behaviour, but we are unsure the
- // previous behaviour was correct. At the time of the fix, anomaly detection had no owner.
- // Whoever next works on it should look into the cases where it is triggered in this function.
- // Discussion here: http://ag/6124370.
- bool useAnomalyDetection = true;
-
- dimensionsInWhatInfo.hasCurrentState = true;
- dimensionsInWhatInfo.currentState = stateKey;
- for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
- const Matcher& matcher = mFieldMatchers[i];
- BaseInfo& baseInfo = baseInfos[i];
- Interval& interval = intervals[i];
- interval.valueIndex = i;
- Value value;
- if (!getDoubleOrLong(event, matcher, value)) {
- VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
- StatsdStats::getInstance().noteBadValueType(mMetricId);
- return;
- }
- interval.seenNewData = true;
-
- if (mUseDiff) {
- if (!baseInfo.hasBase) {
- if (mHasGlobalBase && mUseZeroDefaultBase) {
- // The bucket has global base. This key does not.
- // Optionally use zero as base.
- baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
- baseInfo.hasBase = true;
- } else {
- // no base. just update base and return.
- baseInfo.base = value;
- baseInfo.hasBase = true;
- // If we're missing a base, do not use anomaly detection on incomplete data
- useAnomalyDetection = false;
- // Continue (instead of return) here in order to set baseInfo.base and
- // baseInfo.hasBase for other baseInfos
- continue;
- }
- }
-
- Value diff;
- switch (mValueDirection) {
- case ValueMetric::INCREASING:
- if (value >= baseInfo.base) {
- diff = value - baseInfo.base;
- } else if (mUseAbsoluteValueOnReset) {
- diff = value;
- } else {
- VLOG("Unexpected decreasing value");
- StatsdStats::getInstance().notePullDataError(mPullTagId);
- baseInfo.base = value;
- // If we've got bad data, do not use anomaly detection
- useAnomalyDetection = false;
- continue;
- }
- break;
- case ValueMetric::DECREASING:
- if (baseInfo.base >= value) {
- diff = baseInfo.base - value;
- } else if (mUseAbsoluteValueOnReset) {
- diff = value;
- } else {
- VLOG("Unexpected increasing value");
- StatsdStats::getInstance().notePullDataError(mPullTagId);
- baseInfo.base = value;
- // If we've got bad data, do not use anomaly detection
- useAnomalyDetection = false;
- continue;
- }
- break;
- case ValueMetric::ANY:
- diff = value - baseInfo.base;
- break;
- default:
- break;
- }
- baseInfo.base = value;
- value = diff;
- }
-
- if (interval.hasValue) {
- switch (mAggregationType) {
- case ValueMetric::SUM:
- // for AVG, we add up and take average when flushing the bucket
- case ValueMetric::AVG:
- interval.value += value;
- break;
- case ValueMetric::MIN:
- interval.value = std::min(value, interval.value);
- break;
- case ValueMetric::MAX:
- interval.value = std::max(value, interval.value);
- break;
- default:
- break;
- }
- } else {
- interval.value = value;
- interval.hasValue = true;
- }
- interval.sampleSize += 1;
- }
-
- // State change.
- if (!mSlicedStateAtoms.empty() && stateChange) {
- // Turn OFF the condition timer for the previous state key.
- mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]
- .conditionTimer.onConditionChanged(false, eventTimeNs);
-
- // Turn ON the condition timer for the new state key.
- mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)]
- .conditionTimer.onConditionChanged(true, eventTimeNs);
- }
-
- // Only trigger the tracker if all intervals are correct and we have not skipped the bucket due
- // to MULTIPLE_BUCKETS_SKIPPED.
- if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) {
- // TODO: propgate proper values down stream when anomaly support doubles
- long wholeBucketVal = intervals[0].value.long_value;
- auto prev = mCurrentFullBucket.find(eventKey);
- if (prev != mCurrentFullBucket.end()) {
- wholeBucketVal += prev->second;
- }
- for (auto& tracker : mAnomalyTrackers) {
- tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
- wholeBucketVal);
- }
- }
-}
-
-// For pulled metrics, we always need to make sure we do a pull before flushing the bucket
-// if mCondition is true!
-void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
- int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
- if (eventTimeNs < currentBucketEndTimeNs) {
- VLOG("eventTime is %lld, less than current bucket end time %lld", (long long)eventTimeNs,
- (long long)(currentBucketEndTimeNs));
- return;
- }
- int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
- int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
- flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs);
-}
-
-int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const {
- int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
- if (eventTimeNs < currentBucketEndTimeNs) {
- return 0;
- }
- return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
-}
-
-void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) {
- if (mCondition == ConditionState::kUnknown) {
- StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
- invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
- }
-
- VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
- (int)mCurrentSlicedBucket.size());
-
- int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
- int64_t bucketEndTime = fullBucketEndTimeNs;
- int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
-
- if (multipleBucketsSkipped(numBucketsForward)) {
- VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
- StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
- // Something went wrong. Maybe the device was sleeping for a long time. It is better
- // to mark the current bucket as invalid. The last pull might have been successful through.
- invalidateCurrentBucketWithoutResetBase(eventTimeNs,
- BucketDropReason::MULTIPLE_BUCKETS_SKIPPED);
- // End the bucket at the next bucket start time so the entire interval is skipped.
- bucketEndTime = nextBucketStartTimeNs;
- } else if (eventTimeNs < fullBucketEndTimeNs) {
- bucketEndTime = eventTimeNs;
- }
-
- // Close the current bucket.
- int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
- bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
- if (!isBucketLargeEnough) {
- skipCurrentBucket(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL);
- }
- if (!mCurrentBucketIsSkipped) {
- bool bucketHasData = false;
- // The current bucket is large enough to keep.
- for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
- PastValueBucket bucket =
- buildPartialBucket(bucketEndTime, currentValueBucket.intervals);
- if (!mSlicedStateAtoms.empty()) {
- bucket.mConditionTrueNs =
- currentValueBucket.conditionTimer.newBucketStart(bucketEndTime);
- } else {
- bucket.mConditionTrueNs = conditionTrueDuration;
- }
- // it will auto create new vector of ValuebucketInfo if the key is not found.
- if (bucket.valueIndex.size() > 0) {
- auto& bucketList = mPastBuckets[metricDimensionKey];
- bucketList.push_back(bucket);
- bucketHasData = true;
- }
- }
- if (!bucketHasData) {
- skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA);
- }
- }
-
- if (mCurrentBucketIsSkipped) {
- mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
- mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
- }
-
- // This means that the current bucket was not flushed before a forced bucket split.
- // This can happen if an app update or a dump report with include_current_partial_bucket is
- // requested before we get a chance to flush the bucket due to receiving new data, either from
- // the statsd socket or the StatsPullerManager.
- if (bucketEndTime < nextBucketStartTimeNs) {
- SkippedBucket bucketInGap;
- bucketInGap.bucketStartTimeNs = bucketEndTime;
- bucketInGap.bucketEndTimeNs = nextBucketStartTimeNs;
- bucketInGap.dropEvents.emplace_back(
- buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
- mSkippedBuckets.emplace_back(bucketInGap);
- }
- appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
- initCurrentSlicedBucket(nextBucketStartTimeNs);
- // Update the condition timer again, in case we skipped buckets.
- mConditionTimer.newBucketStart(nextBucketStartTimeNs);
-
- // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing
- // by state. Otherwise, the "global" condition timer will be used.
- if (!mSlicedStateAtoms.empty()) {
- for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
- currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs);
- }
- }
- mCurrentBucketNum += numBucketsForward;
-}
-
-PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
- const std::vector<Interval>& intervals) {
- PastValueBucket bucket;
- bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
- bucket.mBucketEndNs = bucketEndTime;
- for (const auto& interval : intervals) {
- if (interval.hasValue) {
- // skip the output if the diff is zero
- if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) {
- continue;
- }
- bucket.valueIndex.push_back(interval.valueIndex);
- if (mAggregationType != ValueMetric::AVG) {
- bucket.values.push_back(interval.value);
- } else {
- double sum = interval.value.type == LONG ? (double)interval.value.long_value
- : interval.value.double_value;
- bucket.values.push_back(Value((double)sum / interval.sampleSize));
- }
- }
- }
- return bucket;
-}
-
-void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) {
- StatsdStats::getInstance().noteBucketCount(mMetricId);
- // Cleanup data structure to aggregate values.
- for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
- bool obsolete = true;
- for (auto& interval : it->second.intervals) {
- interval.hasValue = false;
- interval.sampleSize = 0;
- if (interval.seenNewData) {
- obsolete = false;
- }
- interval.seenNewData = false;
- }
-
- if (obsolete && !mSlicedStateAtoms.empty()) {
- // When slicing by state, only delete the MetricDimensionKey when the
- // state key in the MetricDimensionKey is not the current state key.
- const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat();
- const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey);
-
- if ((currentBaseInfoItr != mCurrentBaseInfo.end()) &&
- (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) {
- obsolete = false;
- }
- }
- if (obsolete) {
- it = mCurrentSlicedBucket.erase(it);
- } else {
- it++;
- }
- // TODO(b/157655103): remove mCurrentBaseInfo entries when obsolete
- }
-
- mCurrentBucketIsSkipped = false;
- mCurrentSkippedBucket.reset();
-
- // If we do not have a global base when the condition is true,
- // we will have incomplete bucket for the next bucket.
- if (mUseDiff && !mHasGlobalBase && mCondition) {
- mCurrentBucketIsSkipped = false;
- }
- mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
- VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
- (long long)mCurrentBucketStartTimeNs);
-}
-
-void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) {
- if (mCurrentBucketIsSkipped) {
- if (isFullBucketReached) {
- // If the bucket is invalid, we ignore the full bucket since it contains invalid data.
- mCurrentFullBucket.clear();
- }
- // Current bucket is invalid, we do not add it to the full bucket.
- return;
- }
-
- if (isFullBucketReached) { // If full bucket, send to anomaly tracker.
- // Accumulate partial buckets with current value and then send to anomaly tracker.
- if (mCurrentFullBucket.size() > 0) {
- for (const auto& slice : mCurrentSlicedBucket) {
- if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) {
- continue;
- }
- // TODO: fix this when anomaly can accept double values
- auto& interval = slice.second.intervals[0];
- if (interval.hasValue) {
- mCurrentFullBucket[slice.first] += interval.value.long_value;
- }
- }
- for (const auto& slice : mCurrentFullBucket) {
- for (auto& tracker : mAnomalyTrackers) {
- if (tracker != nullptr) {
- tracker->addPastBucket(slice.first, slice.second, mCurrentBucketNum);
- }
- }
- }
- mCurrentFullBucket.clear();
- } else {
- // Skip aggregating the partial buckets since there's no previous partial bucket.
- for (const auto& slice : mCurrentSlicedBucket) {
- for (auto& tracker : mAnomalyTrackers) {
- if (tracker != nullptr && !slice.second.intervals.empty()) {
- // TODO: fix this when anomaly can accept double values
- auto& interval = slice.second.intervals[0];
- if (interval.hasValue) {
- tracker->addPastBucket(slice.first, interval.value.long_value,
- mCurrentBucketNum);
- }
- }
- }
- }
- }
- } else {
- // Accumulate partial bucket.
- for (const auto& slice : mCurrentSlicedBucket) {
- if (!slice.second.intervals.empty()) {
- // TODO: fix this when anomaly can accept double values
- auto& interval = slice.second.intervals[0];
- if (interval.hasValue) {
- mCurrentFullBucket[slice.first] += interval.value.long_value;
- }
- }
- }
- }
-}
-
-size_t ValueMetricProducer::byteSizeLocked() const {
- size_t totalSize = 0;
- for (const auto& pair : mPastBuckets) {
- totalSize += pair.second.size() * kBucketSize;
- }
- return totalSize;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
deleted file mode 100644
index ebd8fecd55d0..000000000000
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ /dev/null
@@ -1,396 +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.
- */
-
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include "anomaly/AnomalyTracker.h"
-#include "condition/ConditionTimer.h"
-#include "condition/ConditionTracker.h"
-#include "external/PullDataReceiver.h"
-#include "external/StatsPullerManager.h"
-#include "matchers/EventMatcherWizard.h"
-#include "stats_log_util.h"
-#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-struct PastValueBucket {
- int64_t mBucketStartNs;
- int64_t mBucketEndNs;
- std::vector<int> valueIndex;
- std::vector<Value> values;
- // If the metric has no condition, then this field is just wasted.
- // When we tune statsd memory usage in the future, this is a candidate to optimize.
- int64_t mConditionTrueNs;
-};
-
-// Aggregates values within buckets.
-//
-// There are different events that might complete a bucket
-// - a condition change
-// - an app upgrade
-// - an alarm set to the end of the bucket
-class ValueMetricProducer : public MetricProducer, public virtual PullDataReceiver {
-public:
- ValueMetricProducer(
- const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
- const vector<ConditionState>& initialConditionCache,
- const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
- 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();
-
- // Process data pulled on bucket boundary.
- void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data,
- bool pullSuccess, int64_t originalPullTimeNs) override;
-
- // ValueMetric needs special logic if it's a pulled atom.
- void notifyAppUpgrade(const int64_t& eventTimeNs) override {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mSplitBucketForAppUpgrade) {
- return;
- }
- if (mIsPulled && mCondition == ConditionState::kTrue) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- };
-
- // ValueMetric needs special logic if it's a pulled atom.
- void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mIsPulled && mCondition == ConditionState::kTrue) {
- pullAndMatchEventsLocked(eventTimeNs);
- }
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- };
-
- void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
- const FieldValue& oldState, const FieldValue& newState) override;
-
- MetricType getMetricType() const override {
- return METRIC_TYPE_VALUE;
- }
-
-protected:
- void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition, const LogEvent& event,
- const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
-
-private:
- void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) override;
- void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
-
- // Internal interface to handle active state change.
- void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
-
- // Internal interface to handle condition change.
- void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
-
- // Internal interface to handle sliced condition change.
- void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
-
- // Internal function to calculate the current used bytes.
- size_t byteSizeLocked() const override;
-
- void dumpStatesLocked(FILE* out, bool verbose) const override;
-
- // For pulled metrics, this method should only be called if a pull has be done. Else we will
- // not have complete data for the bucket.
- void flushIfNeededLocked(const int64_t& eventTime) override;
-
- // For pulled metrics, this method should only be called if a pulled have be done. Else we will
- // not have complete data for the bucket.
- void flushCurrentBucketLocked(const int64_t& eventTimeNs,
- const int64_t& nextBucketStartTimeNs) override;
-
- void prepareFirstBucketLocked() override;
-
- void dropDataLocked(const int64_t dropTimeNs) override;
-
- // Calculate previous bucket end time based on current time.
- int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs);
-
- // Calculate how many buckets are present between the current bucket and eventTimeNs.
- int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const;
-
- // Mark the data as invalid.
- void invalidateCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
-
- void invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs,
- const BucketDropReason reason);
-
- // Skips the current bucket without notifying StatsdStats of the skipped bucket.
- // This should only be called from #flushCurrentBucketLocked. Otherwise, a future event that
- // causes the bucket to be invalidated will not notify StatsdStats.
- void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
-
- bool onConfigUpdatedLocked(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation) override;
-
- int mWhatMatcherIndex;
-
- sp<EventMatcherWizard> mEventMatcherWizard;
-
- sp<StatsPullerManager> mPullerManager;
-
- // Value fields for matching.
- std::vector<Matcher> mFieldMatchers;
-
- // Value fields for matching.
- std::set<HashableDimensionKey> mMatchedMetricDimensionKeys;
-
- // Holds the atom id, primary key pair from a state change.
- pair<int32_t, HashableDimensionKey> mStateChangePrimaryKey;
-
- // tagId for pulled data. -1 if this is not pulled
- const int mPullTagId;
-
- // if this is pulled metric
- const bool mIsPulled;
-
- // Tracks the value information of one value field.
- typedef struct {
- // Index in multi value aggregation.
- int valueIndex;
- // Current value, depending on the aggregation type.
- Value value;
- // Number of samples collected.
- int sampleSize;
- // If this dimension has any non-tainted value. If not, don't report the
- // dimension.
- bool hasValue = false;
- // Whether new data is seen in the bucket.
- bool seenNewData = false;
- } Interval;
-
- // Internal state of an ongoing aggregation bucket.
- typedef struct CurrentValueBucket {
- // If the `MetricDimensionKey` state key is the current state key, then
- // the condition timer will be updated later (e.g. condition/state/active
- // state change) with the correct condition and time.
- CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {}
- // Value information for each value field of the metric.
- std::vector<Interval> intervals;
- // Tracks how long the condition is true.
- ConditionTimer conditionTimer;
- } CurrentValueBucket;
-
- // Holds base information for diffing values from one value field.
- typedef struct {
- // Holds current base value of the dimension. Take diff and update if necessary.
- Value base;
- // Whether there is a base to diff to.
- bool hasBase;
- } BaseInfo;
-
- // State key and base information for a specific DimensionsInWhat key.
- typedef struct DimensionsInWhatInfo {
- DimensionsInWhatInfo(const HashableDimensionKey& stateKey)
- : baseInfos(), currentState(stateKey), hasCurrentState(false) {
- }
- std::vector<BaseInfo> baseInfos;
- // Last seen state value(s).
- HashableDimensionKey currentState;
- // Whether this dimensions in what key has a current state key.
- bool hasCurrentState;
- } DimensionsInWhatInfo;
-
- // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat
- // key and StateValuesKey pair.
- std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket;
-
- // Tracks current state key and base information for each DimensionsInWhat key.
- std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mCurrentBaseInfo;
-
- std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
-
- // Save the past buckets and we can clear when the StatsLogReport is dumped.
- std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets;
-
- const int64_t mMinBucketSizeNs;
-
- // Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const MetricDimensionKey& newKey);
-
- bool hasReachedGuardRailLimit() const;
-
- bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
-
- void pullAndMatchEventsLocked(const int64_t timestampNs);
-
- bool multipleBucketsSkipped(const int64_t numBucketsForward);
-
- void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
-
- PastValueBucket buildPartialBucket(int64_t bucketEndTime,
- const std::vector<Interval>& intervals);
-
- void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
-
- void appendToFullBucket(const bool isFullBucketReached);
-
- // Reset diff base and mHasGlobalBase
- void resetBase();
-
- // Updates the condition timers in the current sliced bucket when there is a
- // condition change or an active state change.
- void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs);
-
- static const size_t kBucketSize = sizeof(PastValueBucket{});
-
- const size_t mDimensionSoftLimit;
-
- const size_t mDimensionHardLimit;
-
- const bool mUseAbsoluteValueOnReset;
-
- const ValueMetric::AggregationType mAggregationType;
-
- const bool mUseDiff;
-
- const ValueMetric::ValueDirection mValueDirection;
-
- const bool mSkipZeroDiffOutput;
-
- // If true, use a zero value as base to compute the diff.
- // This is used for new keys which are present in the new data but was not
- // present in the base data.
- // The default base will only be used if we have a global base.
- const bool mUseZeroDefaultBase;
-
- // For pulled metrics, this is always set to true whenever a pull succeeds.
- // It is set to false when a pull fails, or upon condition change to false.
- // This is used to decide if we have the right base data to compute the
- // diff against.
- bool mHasGlobalBase;
-
- // This is to track whether or not the bucket is skipped for any of the reasons listed in
- // BucketDropReason, many of which make the bucket potentially invalid.
- bool mCurrentBucketIsSkipped;
-
- const int64_t mMaxPullDelayNs;
-
- const bool mSplitBucketForAppUpgrade;
-
- ConditionTimer mConditionTimer;
-
- FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
- FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet);
- FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
- FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged);
- FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary);
- FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged);
- FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
- FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
- FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff);
- FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff);
- FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
- FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
- FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedState);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
- FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
- FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket);
- FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary);
-
- FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
- FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
- FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed);
- FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit);
- FRIEND_TEST(ValueMetricProducerTest_BucketDrop,
- TestInvalidBucketWhenAccumulateEventWrongBucket);
-
- FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket);
- FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid);
- FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated);
- FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPushedEvents);
- FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue);
- FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse);
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateValueMetrics);
-
- friend class ValueMetricProducerTestHelper;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
deleted file mode 100644
index cf1f437c4168..000000000000
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ /dev/null
@@ -1,240 +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.
- */
-
-#ifndef DURATION_TRACKER_H
-#define DURATION_TRACKER_H
-
-#include "anomaly/DurationAnomalyTracker.h"
-#include "condition/ConditionWizard.h"
-#include "config/ConfigKey.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-enum DurationState {
- kStopped = 0, // The event is stopped.
- kStarted = 1, // The event is on going.
- kPaused = 2, // The event is started, but condition is false, clock is paused. When condition
- // turns to true, kPaused will become kStarted.
-};
-
-// Hold duration information for one atom level duration in current on-going bucket.
-struct DurationInfo {
- DurationState state;
-
- // the number of starts seen.
- int32_t startCount;
-
- // most recent start time.
- int64_t lastStartTime;
- // existing duration in current bucket.
- int64_t lastDuration;
- // cache the HashableDimensionKeys we need to query the condition for this duration event.
- ConditionKey conditionKeys;
-
- DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
-};
-
-struct DurationBucket {
- int64_t mBucketStartNs;
- int64_t mBucketEndNs;
- int64_t mDuration;
-};
-
-struct DurationValues {
- // Recorded duration for current partial bucket.
- int64_t mDuration;
-
- // Sum of past partial bucket durations in current full bucket.
- // Used for anomaly detection.
- int64_t mDurationFullBucket;
-};
-
-class DurationTracker {
-public:
- DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
- int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
- int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
- const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
- : mConfigKey(key),
- mTrackerId(id),
- mEventKey(eventKey),
- mWizard(wizard),
- mConditionTrackerIndex(conditionIndex),
- mBucketSizeNs(bucketSizeNs),
- mNested(nesting),
- mCurrentBucketStartTimeNs(currentBucketStartNs),
- mDuration(0),
- mCurrentBucketNum(currentBucketNum),
- mStartTimeNs(startTimeNs),
- mConditionSliced(conditionSliced),
- mHasLinksToAllConditionDimensionsInTracker(fullLink),
- mAnomalyTrackers(anomalyTrackers){};
-
- virtual ~DurationTracker(){};
-
- void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) {
- sp<ConditionWizard> tmpWizard = mWizard;
- mWizard = wizard;
- mConditionTrackerIndex = conditionTrackerIndex;
- mAnomalyTrackers.clear();
- };
-
- virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
- const ConditionKey& conditionKey) = 0;
- virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
- const bool stopAll) = 0;
- virtual void noteStopAll(const int64_t eventTime) = 0;
-
- virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0;
- virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
-
- virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
- const FieldValue& newState) = 0;
-
- // Flush stale buckets if needed, and return true if the tracker has no on-going duration
- // events, so that the owner can safely remove the tracker.
- virtual bool flushIfNeeded(
- int64_t timestampNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
-
- // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from
- // an app upgrade, we assume that we're trying to form a partial bucket.
- virtual bool flushCurrentBucket(
- const int64_t& eventTimeNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
-
- // Predict the anomaly timestamp given the current status.
- virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
- const int64_t currentTimestamp) const = 0;
- // Dump internal states for debugging
- virtual void dumpStates(FILE* out, bool verbose) const = 0;
-
- virtual int64_t getCurrentStateKeyDuration() const = 0;
-
- virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
-
- // Replace old value with new value for the given state atom.
- virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
-
- void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
- mAnomalyTrackers.push_back(anomalyTracker);
- }
-
-protected:
- int64_t getCurrentBucketEndTimeNs() const {
- return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
- }
-
- // Starts the anomaly alarm.
- void startAnomalyAlarm(const int64_t eventTime) {
- for (auto& anomalyTracker : mAnomalyTrackers) {
- if (anomalyTracker != nullptr) {
- const int64_t alarmTimestampNs =
- predictAnomalyTimestampNs(*anomalyTracker, eventTime);
- if (alarmTimestampNs > 0) {
- anomalyTracker->startAlarm(mEventKey, alarmTimestampNs);
- }
- }
- }
- }
-
- // Stops the anomaly alarm. If it should have already fired, declare the anomaly now.
- void stopAnomalyAlarm(const int64_t timestamp) {
- for (auto& anomalyTracker : mAnomalyTrackers) {
- if (anomalyTracker != nullptr) {
- anomalyTracker->stopAlarm(mEventKey, timestamp);
- }
- }
- }
-
- void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,
- const int64_t& bucketValue, const int64_t& bucketNum) {
- for (auto& anomalyTracker : mAnomalyTrackers) {
- if (anomalyTracker != nullptr) {
- anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
- }
- }
- }
-
- void detectAndDeclareAnomaly(const int64_t& timestamp, const int64_t& currBucketNum,
- const int64_t& currentBucketValue) {
- for (auto& anomalyTracker : mAnomalyTrackers) {
- if (anomalyTracker != nullptr) {
- anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
- mEventKey, currentBucketValue);
- }
- }
- }
-
- // Convenience to compute the current bucket's end time, which is always aligned with the
- // start time of the metric.
- int64_t getCurrentBucketEndTimeNs() {
- return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
- }
-
- void setEventKey(const MetricDimensionKey& eventKey) {
- mEventKey = eventKey;
- }
-
- // A reference to the DurationMetricProducer's config key.
- const ConfigKey& mConfigKey;
-
- const int64_t mTrackerId;
-
- MetricDimensionKey mEventKey;
-
- sp<ConditionWizard> mWizard;
-
- int mConditionTrackerIndex;
-
- const int64_t mBucketSizeNs;
-
- const bool mNested;
-
- int64_t mCurrentBucketStartTimeNs;
-
- int64_t mDuration; // current recorded duration result (for partial bucket)
-
- // Recorded duration results for each state key in the current partial bucket.
- std::unordered_map<HashableDimensionKey, DurationValues> mStateKeyDurationMap;
-
- int64_t mCurrentBucketNum;
-
- const int64_t mStartTimeNs;
-
- const bool mConditionSliced;
-
- bool mHasLinksToAllConditionDimensionsInTracker;
-
- std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
-
- FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
- FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
- FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
-
- FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
- FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // DURATION_TRACKER_H
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
deleted file mode 100644
index 62f49824b874..000000000000
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ /dev/null
@@ -1,331 +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.
- */
-
-#define DEBUG false
-
-#include "Log.h"
-#include "MaxDurationTracker.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
- const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
- int64_t currentBucketStartNs, int64_t currentBucketNum,
- int64_t startTimeNs, int64_t bucketSizeNs,
- bool conditionSliced, bool fullLink,
- const vector<sp<AnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
- anomalyTrackers) {
-}
-
-bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
- // ===========GuardRail==============
- if (mInfos.find(newKey) != mInfos.end()) {
- // if the key existed, we are good!
- return false;
- }
- // 1. Report the tuple count if the tuple count > soft limit
- if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mInfos.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("MaxDurTracker %lld dropping data for dimension key %s",
- (long long)mTrackerId, newKey.toString().c_str());
- return true;
- }
- }
- return false;
-}
-
-void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
- const int64_t eventTime, const ConditionKey& conditionKey) {
- // this will construct a new DurationInfo if this key didn't exist.
- if (hitGuardRail(key)) {
- return;
- }
-
- DurationInfo& duration = mInfos[key];
- if (mConditionSliced) {
- duration.conditionKeys = conditionKey;
- }
- VLOG("MaxDuration: key %s start condition %d", key.toString().c_str(), condition);
-
- switch (duration.state) {
- case kStarted:
- duration.startCount++;
- break;
- case kPaused:
- duration.startCount++;
- break;
- case kStopped:
- if (!condition) {
- // event started, but we need to wait for the condition to become true.
- duration.state = DurationState::kPaused;
- } else {
- duration.state = DurationState::kStarted;
- duration.lastStartTime = eventTime;
- startAnomalyAlarm(eventTime);
- }
- duration.startCount = 1;
- break;
- }
-}
-
-void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t eventTime,
- bool forceStop) {
- VLOG("MaxDuration: key %s stop", key.toString().c_str());
- if (mInfos.find(key) == mInfos.end()) {
- // we didn't see a start event before. do nothing.
- return;
- }
- DurationInfo& duration = mInfos[key];
-
- switch (duration.state) {
- case DurationState::kStopped:
- // already stopped, do nothing.
- break;
- case DurationState::kStarted: {
- duration.startCount--;
- if (forceStop || !mNested || duration.startCount <= 0) {
- stopAnomalyAlarm(eventTime);
- duration.state = DurationState::kStopped;
- int64_t durationTime = eventTime - duration.lastStartTime;
- VLOG("Max, key %s, Stop %lld %lld %lld", key.toString().c_str(),
- (long long)duration.lastStartTime, (long long)eventTime,
- (long long)durationTime);
- duration.lastDuration += durationTime;
- if (anyStarted()) {
- // In case any other dimensions are still started, we need to keep the alarm
- // set.
- startAnomalyAlarm(eventTime);
- }
- VLOG(" record duration: %lld ", (long long)duration.lastDuration);
- }
- break;
- }
- case DurationState::kPaused: {
- duration.startCount--;
- if (forceStop || !mNested || duration.startCount <= 0) {
- duration.state = DurationState::kStopped;
- }
- break;
- }
- }
-
- if (duration.lastDuration > mDuration) {
- mDuration = duration.lastDuration;
- VLOG("Max: new max duration: %lld", (long long)mDuration);
- }
- // Once an atom duration ends, we erase it. Next time, if we see another atom event with the
- // same name, they are still considered as different atom durations.
- if (duration.state == DurationState::kStopped) {
- mInfos.erase(key);
- }
-}
-
-bool MaxDurationTracker::anyStarted() {
- for (auto& pair : mInfos) {
- if (pair.second.state == kStarted) {
- return true;
- }
- }
- return false;
-}
-
-void MaxDurationTracker::noteStopAll(const int64_t eventTime) {
- std::set<HashableDimensionKey> keys;
- for (const auto& pair : mInfos) {
- keys.insert(pair.first);
- }
- for (auto& key : keys) {
- noteStop(key, eventTime, true);
- }
-}
-
-bool MaxDurationTracker::flushCurrentBucket(
- const int64_t& eventTimeNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
- VLOG("MaxDurationTracker flushing.....");
-
- // adjust the bucket start time
- int numBucketsForward = 0;
- int64_t fullBucketEnd = getCurrentBucketEndTimeNs();
- int64_t currentBucketEndTimeNs;
- if (eventTimeNs >= fullBucketEnd) {
- numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs;
- currentBucketEndTimeNs = fullBucketEnd;
- } else {
- // This must be a partial bucket.
- currentBucketEndTimeNs = eventTimeNs;
- }
-
- bool hasPendingEvent =
- false; // has either a kStarted or kPaused event across bucket boundaries
- // meaning we need to carry them over to the new bucket.
- for (auto it = mInfos.begin(); it != mInfos.end();) {
- if (it->second.state == DurationState::kStopped) {
- // No need to keep buckets for events that were stopped before.
- it = mInfos.erase(it);
- } else {
- ++it;
- hasPendingEvent = true;
- }
- }
-
- // mDuration is updated in noteStop to the maximum duration that ended in the current bucket.
- if (mDuration != 0) {
- DurationBucket info;
- info.mBucketStartNs = mCurrentBucketStartTimeNs;
- info.mBucketEndNs = currentBucketEndTimeNs;
- info.mDuration = mDuration;
- (*output)[mEventKey].push_back(info);
- VLOG(" final duration for last bucket: %lld", (long long)mDuration);
- }
-
- if (numBucketsForward > 0) {
- mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs;
- mCurrentBucketNum += numBucketsForward;
- } else { // We must be forming a partial bucket.
- mCurrentBucketStartTimeNs = eventTimeNs;
- }
-
- mDuration = 0;
- // If this tracker has no pending events, tell owner to remove.
- return !hasPendingEvent;
-}
-
-bool MaxDurationTracker::flushIfNeeded(
- int64_t eventTimeNs, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
- if (eventTimeNs < getCurrentBucketEndTimeNs()) {
- return false;
- }
- return flushCurrentBucket(eventTimeNs, output);
-}
-
-void MaxDurationTracker::onSlicedConditionMayChange(bool overallCondition,
- const int64_t timestamp) {
- // Now for each of the on-going event, check if the condition has changed for them.
- for (auto& pair : mInfos) {
- if (pair.second.state == kStopped) {
- continue;
- }
- ConditionState conditionState = mWizard->query(
- mConditionTrackerIndex, pair.second.conditionKeys,
- !mHasLinksToAllConditionDimensionsInTracker);
- bool conditionMet = (conditionState == ConditionState::kTrue);
-
- VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet);
- noteConditionChanged(pair.first, conditionMet, timestamp);
- }
-}
-
-void MaxDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
- const FieldValue& newState) {
- ALOGE("MaxDurationTracker does not handle sliced state changes.");
-}
-
-void MaxDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) {
- for (auto& pair : mInfos) {
- noteConditionChanged(pair.first, condition, timestamp);
- }
-}
-
-void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
- const int64_t timestamp) {
- auto it = mInfos.find(key);
- if (it == mInfos.end()) {
- return;
- }
-
- switch (it->second.state) {
- case kStarted:
- // If condition becomes false, kStarted -> kPaused. Record the current duration and
- // stop anomaly alarm.
- if (!conditionMet) {
- stopAnomalyAlarm(timestamp);
- it->second.state = DurationState::kPaused;
- it->second.lastDuration += (timestamp - it->second.lastStartTime);
- if (anyStarted()) {
- // In case any other dimensions are still started, we need to set the alarm.
- startAnomalyAlarm(timestamp);
- }
- VLOG("MaxDurationTracker Key: %s Started->Paused ", key.toString().c_str());
- }
- break;
- case kStopped:
- // Nothing to do if it's stopped.
- break;
- case kPaused:
- // If condition becomes true, kPaused -> kStarted. and the start time is the condition
- // change time.
- if (conditionMet) {
- it->second.state = DurationState::kStarted;
- it->second.lastStartTime = timestamp;
- startAnomalyAlarm(timestamp);
- VLOG("MaxDurationTracker Key: %s Paused->Started", key.toString().c_str());
- }
- break;
- }
- // Note that we don't update mDuration here since it's only updated during noteStop.
-}
-
-int64_t MaxDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
- const int64_t currentTimestamp) const {
- // The allowed time we can continue in the current state is the
- // (anomaly threshold) - max(elapsed time of the started mInfos).
- int64_t maxElapsed = 0;
- for (auto it = mInfos.begin(); it != mInfos.end(); ++it) {
- if (it->second.state == DurationState::kStarted) {
- int64_t duration =
- it->second.lastDuration + (currentTimestamp - it->second.lastStartTime);
- if (duration > maxElapsed) {
- maxElapsed = duration;
- }
- }
- }
- int64_t anomalyTimeNs = currentTimestamp + anomalyTracker.getAnomalyThreshold() - maxElapsed;
- int64_t refractoryEndNs = anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC;
- return std::max(anomalyTimeNs, refractoryEndNs);
-}
-
-void MaxDurationTracker::dumpStates(FILE* out, bool verbose) const {
- fprintf(out, "\t\t sub-durations %lu\n", (unsigned long)mInfos.size());
- fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
-}
-
-int64_t MaxDurationTracker::getCurrentStateKeyDuration() const {
- ALOGE("MaxDurationTracker does not handle sliced state changes.");
- return -1;
-}
-
-int64_t MaxDurationTracker::getCurrentStateKeyFullBucketDuration() const {
- ALOGE("MaxDurationTracker does not handle sliced state changes.");
- return -1;
-}
-
-void MaxDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
- ALOGE("MaxDurationTracker does not handle sliced state changes.");
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
deleted file mode 100644
index be2707c60c1b..000000000000
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ /dev/null
@@ -1,92 +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.
- */
-
-#ifndef MAX_DURATION_TRACKER_H
-#define MAX_DURATION_TRACKER_H
-
-#include "DurationTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Tracks a pool of atom durations, and output the max duration for each bucket.
-// To get max duration, we need to keep track of each individual durations, and compare them when
-// they stop or bucket expires.
-class MaxDurationTracker : public DurationTracker {
-public:
- MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
- int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
- int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
- const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
-
- MaxDurationTracker(const MaxDurationTracker& tracker) = default;
-
- void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
- const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
- const bool stopAll) override;
- void noteStopAll(const int64_t eventTime) override;
-
- bool flushIfNeeded(
- int64_t timestampNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
- bool flushCurrentBucket(
- const int64_t& eventTimeNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>*) override;
-
- void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
- void onConditionChanged(bool condition, const int64_t timestamp) override;
-
- void onStateChanged(const int64_t timestamp, const int32_t atomId,
- const FieldValue& newState) override;
-
- int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
- const int64_t currentTimestamp) const override;
- void dumpStates(FILE* out, bool verbose) const override;
-
- int64_t getCurrentStateKeyDuration() const override;
-
- int64_t getCurrentStateKeyFullBucketDuration() const override;
-
- void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
-
-private:
- // Returns true if at least one of the mInfos is started.
- bool anyStarted();
-
- std::unordered_map<HashableDimensionKey, DurationInfo> mInfos;
-
- void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
- const int64_t timestamp);
-
- // return true if we should not allow newKey to be tracked because we are above the threshold
- bool hitGuardRail(const HashableDimensionKey& newKey);
-
- FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
- FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
- FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
- FRIEND_TEST(MaxDurationTrackerTest, TestStopAll);
- FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection);
- FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // MAX_DURATION_TRACKER_H
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
deleted file mode 100644
index 247e2e01c992..000000000000
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ /dev/null
@@ -1,462 +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.
- */
-#define DEBUG false
-#include "Log.h"
-#include "OringDurationTracker.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::pair;
-
-OringDurationTracker::OringDurationTracker(
- const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs,
- int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
- bool fullLink, const vector<sp<AnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
- anomalyTrackers),
- mStarted(),
- mPaused() {
- mLastStartTime = 0;
-}
-
-bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
- // ===========GuardRail==============
- // 1. Report the tuple count if the tuple count > soft limit
- if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) {
- return false;
- }
- if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mConditionKeyMap.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mTrackerId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("OringDurTracker %lld dropping data for dimension key %s",
- (long long)mTrackerId, newKey.toString().c_str());
- return true;
- }
- }
- return false;
-}
-
-void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
- const int64_t eventTime, const ConditionKey& conditionKey) {
- if (hitGuardRail(key)) {
- return;
- }
- if (condition) {
- if (mStarted.size() == 0) {
- mLastStartTime = eventTime;
- VLOG("record first start....");
- startAnomalyAlarm(eventTime);
- }
- mStarted[key]++;
- } else {
- mPaused[key]++;
- }
-
- if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
- mConditionKeyMap[key] = conditionKey;
- }
- VLOG("Oring: %s start, condition %d", key.toString().c_str(), condition);
-}
-
-void OringDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t timestamp,
- const bool stopAll) {
- VLOG("Oring: %s stop", key.toString().c_str());
- auto it = mStarted.find(key);
- if (it != mStarted.end()) {
- (it->second)--;
- if (stopAll || !mNested || it->second <= 0) {
- mStarted.erase(it);
- mConditionKeyMap.erase(key);
- }
- if (mStarted.empty()) {
- mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
- (timestamp - mLastStartTime);
- detectAndDeclareAnomaly(
- timestamp, mCurrentBucketNum,
- getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
- VLOG("record duration %lld, total duration %lld for state key %s",
- (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
- mEventKey.getStateValuesKey().toString().c_str());
- }
- }
-
- auto pausedIt = mPaused.find(key);
- if (pausedIt != mPaused.end()) {
- (pausedIt->second)--;
- if (stopAll || !mNested || pausedIt->second <= 0) {
- mPaused.erase(pausedIt);
- mConditionKeyMap.erase(key);
- }
- }
- if (mStarted.empty()) {
- stopAnomalyAlarm(timestamp);
- }
-}
-
-void OringDurationTracker::noteStopAll(const int64_t timestamp) {
- if (!mStarted.empty()) {
- mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
- (timestamp - mLastStartTime);
- VLOG("Oring Stop all: record duration %lld, total duration %lld for state key %s",
- (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
- mEventKey.getStateValuesKey().toString().c_str());
- detectAndDeclareAnomaly(
- timestamp, mCurrentBucketNum,
- getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
- }
-
- stopAnomalyAlarm(timestamp);
- mStarted.clear();
- mPaused.clear();
- mConditionKeyMap.clear();
-}
-
-bool OringDurationTracker::flushCurrentBucket(
- const int64_t& eventTimeNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
- VLOG("OringDurationTracker Flushing.............");
-
- // Note that we have to mimic the bucket time changes we do in the
- // MetricProducer#notifyAppUpgrade.
-
- int numBucketsForward = 0;
- int64_t fullBucketEnd = getCurrentBucketEndTimeNs();
- int64_t currentBucketEndTimeNs;
-
- if (eventTimeNs >= fullBucketEnd) {
- numBucketsForward = 1 + (eventTimeNs - fullBucketEnd) / mBucketSizeNs;
- currentBucketEndTimeNs = fullBucketEnd;
- } else {
- // This must be a partial bucket.
- currentBucketEndTimeNs = eventTimeNs;
- }
-
- // Process the current bucket.
- if (mStarted.size() > 0) {
- // Calculate the duration for the current state key.
- mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
- (currentBucketEndTimeNs - mLastStartTime);
- }
- // Store DurationBucket info for each whatKey, stateKey pair.
- // Note: The whatKey stored in mEventKey is constant for each DurationTracker, while the
- // stateKey stored in mEventKey is only the current stateKey. mStateKeyDurationMap is used to
- // store durations for each stateKey, so we need to flush the bucket by creating a
- // DurationBucket for each stateKey.
- for (auto& durationIt : mStateKeyDurationMap) {
- if (durationIt.second.mDuration > 0) {
- DurationBucket current_info;
- current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
- current_info.mBucketEndNs = currentBucketEndTimeNs;
- current_info.mDuration = durationIt.second.mDuration;
- (*output)[MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first)]
- .push_back(current_info);
-
- durationIt.second.mDurationFullBucket += durationIt.second.mDuration;
- VLOG(" duration: %lld", (long long)current_info.mDuration);
- }
-
- if (eventTimeNs > fullBucketEnd) {
- // End of full bucket, can send to anomaly tracker now.
- addPastBucketToAnomalyTrackers(
- MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first),
- getCurrentStateKeyFullBucketDuration(), mCurrentBucketNum);
- durationIt.second.mDurationFullBucket = 0;
- }
- durationIt.second.mDuration = 0;
- }
-
- if (mStarted.size() > 0) {
- for (int i = 1; i < numBucketsForward; i++) {
- DurationBucket info;
- info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1);
- info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
- info.mDuration = mBucketSizeNs;
- // Full duration buckets are attributed to the current stateKey.
- (*output)[mEventKey].push_back(info);
- // Safe to send these buckets to anomaly tracker since they must be full buckets.
- // If it's a partial bucket, numBucketsForward would be 0.
- addPastBucketToAnomalyTrackers(mEventKey, info.mDuration, mCurrentBucketNum + i);
- VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
- }
- } else {
- if (numBucketsForward >= 2) {
- addPastBucketToAnomalyTrackers(mEventKey, 0, mCurrentBucketNum + numBucketsForward - 1);
- }
- }
-
- if (numBucketsForward > 0) {
- mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs;
- mCurrentBucketNum += numBucketsForward;
- } else { // We must be forming a partial bucket.
- mCurrentBucketStartTimeNs = eventTimeNs;
- }
- mLastStartTime = mCurrentBucketStartTimeNs;
-
- // if all stopped, then tell owner it's safe to remove this tracker.
- return mStarted.empty() && mPaused.empty();
-}
-
-bool OringDurationTracker::flushIfNeeded(
- int64_t eventTimeNs, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
- if (eventTimeNs < getCurrentBucketEndTimeNs()) {
- return false;
- }
- return flushCurrentBucket(eventTimeNs, output);
-}
-
-void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition,
- const int64_t timestamp) {
- vector<pair<HashableDimensionKey, int>> startedToPaused;
- vector<pair<HashableDimensionKey, int>> pausedToStarted;
- if (!mStarted.empty()) {
- for (auto it = mStarted.begin(); it != mStarted.end();) {
- const auto& key = it->first;
- const auto& condIt = mConditionKeyMap.find(key);
- if (condIt == mConditionKeyMap.end()) {
- VLOG("Key %s dont have condition key", key.toString().c_str());
- ++it;
- continue;
- }
- ConditionState conditionState =
- mWizard->query(mConditionTrackerIndex, condIt->second,
- !mHasLinksToAllConditionDimensionsInTracker);
- if (conditionState != ConditionState::kTrue) {
- startedToPaused.push_back(*it);
- it = mStarted.erase(it);
- VLOG("Key %s started -> paused", key.toString().c_str());
- } else {
- ++it;
- }
- }
-
- if (mStarted.empty()) {
- mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
- (timestamp - mLastStartTime);
- VLOG("record duration %lld, total duration %lld for state key %s",
- (long long)(timestamp - mLastStartTime), (long long)getCurrentStateKeyDuration(),
- mEventKey.getStateValuesKey().toString().c_str());
- detectAndDeclareAnomaly(
- timestamp, mCurrentBucketNum,
- getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
- }
- }
-
- if (!mPaused.empty()) {
- for (auto it = mPaused.begin(); it != mPaused.end();) {
- const auto& key = it->first;
- if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
- VLOG("Key %s dont have condition key", key.toString().c_str());
- ++it;
- continue;
- }
- ConditionState conditionState =
- mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
- !mHasLinksToAllConditionDimensionsInTracker);
- if (conditionState == ConditionState::kTrue) {
- pausedToStarted.push_back(*it);
- it = mPaused.erase(it);
- VLOG("Key %s paused -> started", key.toString().c_str());
- } else {
- ++it;
- }
- }
-
- if (mStarted.empty() && pausedToStarted.size() > 0) {
- mLastStartTime = timestamp;
- }
- }
-
- if (mStarted.empty() && !pausedToStarted.empty()) {
- startAnomalyAlarm(timestamp);
- }
- mStarted.insert(pausedToStarted.begin(), pausedToStarted.end());
- mPaused.insert(startedToPaused.begin(), startedToPaused.end());
-
- if (mStarted.empty()) {
- stopAnomalyAlarm(timestamp);
- }
-}
-
-void OringDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) {
- if (condition) {
- if (!mPaused.empty()) {
- VLOG("Condition true, all started");
- if (mStarted.empty()) {
- mLastStartTime = timestamp;
- }
- if (mStarted.empty() && !mPaused.empty()) {
- startAnomalyAlarm(timestamp);
- }
- mStarted.insert(mPaused.begin(), mPaused.end());
- mPaused.clear();
- }
- } else {
- if (!mStarted.empty()) {
- VLOG("Condition false, all paused");
- mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
- (timestamp - mLastStartTime);
- mPaused.insert(mStarted.begin(), mStarted.end());
- mStarted.clear();
- detectAndDeclareAnomaly(
- timestamp, mCurrentBucketNum,
- getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
- }
- }
- if (mStarted.empty()) {
- stopAnomalyAlarm(timestamp);
- }
-}
-
-void OringDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
- const FieldValue& newState) {
- // Nothing needs to be done on a state change if we have not seen a start
- // event, the metric is currently not active, or condition is false.
- // For these cases, no keys are being tracked in mStarted, so update
- // the current state key and return.
- if (mStarted.empty()) {
- updateCurrentStateKey(atomId, newState);
- return;
- }
- // Add the current duration length to the previous state key and then update
- // the last start time and current state key.
- mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += (timestamp - mLastStartTime);
- mLastStartTime = timestamp;
- updateCurrentStateKey(atomId, newState);
-}
-
-int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
- const int64_t eventTimestampNs) const {
- // The anomaly threshold.
- const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
-
- // The timestamp of the current bucket end.
- const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs();
-
- // The past duration ns for the current bucket of the current stateKey.
- int64_t currentStateBucketPastNs =
- getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration();
-
- // As we move into the future, old buckets get overwritten (so their old data is erased).
- // Sum of past durations. Will change as we overwrite old buckets.
- int64_t pastNs = currentStateBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
-
- // The refractory period end timestamp for dimension mEventKey.
- const int64_t refractoryPeriodEndNs =
- anomalyTracker.getRefractoryPeriodEndsSec(mEventKey) * NS_PER_SEC;
-
- // The anomaly should happen when accumulated wakelock duration is above the threshold and
- // not within the refractory period.
- int64_t anomalyTimestampNs =
- std::max(eventTimestampNs + thresholdNs - pastNs, refractoryPeriodEndNs);
- // If the predicted the anomaly timestamp is within the current bucket, return it directly.
- if (anomalyTimestampNs <= currentBucketEndNs) {
- return std::max(eventTimestampNs, anomalyTimestampNs);
- }
-
- // Remove the old bucket.
- if (anomalyTracker.getNumOfPastBuckets() > 0) {
- pastNs -= anomalyTracker.getPastBucketValue(
- mEventKey,
- mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets());
- // Add the remaining of the current bucket to the accumulated wakelock duration.
- pastNs += (currentBucketEndNs - eventTimestampNs);
- } else {
- // The anomaly depends on only one bucket.
- pastNs = 0;
- }
-
- // The anomaly will not happen in the current bucket. We need to iterate over the future buckets
- // to predict the accumulated wakelock duration and determine the anomaly timestamp accordingly.
- for (int futureBucketIdx = 1; futureBucketIdx <= anomalyTracker.getNumOfPastBuckets() + 1;
- futureBucketIdx++) {
- // The alarm candidate timestamp should meet two requirements:
- // 1. the accumulated wakelock duration is above the threshold.
- // 2. it is not within the refractory period.
- // 3. the alarm timestamp falls in this bucket. Otherwise we need to flush the past buckets,
- // find the new alarm candidate timestamp and check these requirements again.
- const int64_t bucketEndNs = currentBucketEndNs + futureBucketIdx * mBucketSizeNs;
- int64_t anomalyTimestampNs =
- std::max(bucketEndNs - mBucketSizeNs + thresholdNs - pastNs, refractoryPeriodEndNs);
- if (anomalyTimestampNs <= bucketEndNs) {
- return anomalyTimestampNs;
- }
- if (anomalyTracker.getNumOfPastBuckets() <= 0) {
- continue;
- }
-
- // No valid alarm timestamp is found in this bucket. The clock moves to the end of the
- // bucket. Update the pastNs.
- pastNs += mBucketSizeNs;
- // 1. If the oldest past bucket is still in the past bucket window, we could fetch the past
- // bucket and erase it from pastNs.
- // 2. If the oldest past bucket is the current bucket, we should compute the
- // wakelock duration in the current bucket and erase it from pastNs.
- // 3. Otherwise all othe past buckets are ancient.
- if (futureBucketIdx < anomalyTracker.getNumOfPastBuckets()) {
- pastNs -= anomalyTracker.getPastBucketValue(
- mEventKey,
- mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx);
- } else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) {
- pastNs -= (currentStateBucketPastNs + (currentBucketEndNs - eventTimestampNs));
- }
- }
-
- return std::max(eventTimestampNs + thresholdNs, refractoryPeriodEndNs);
-}
-
-void OringDurationTracker::dumpStates(FILE* out, bool verbose) const {
- fprintf(out, "\t\t started count %lu\n", (unsigned long)mStarted.size());
- fprintf(out, "\t\t paused count %lu\n", (unsigned long)mPaused.size());
- fprintf(out, "\t\t current duration %lld\n", (long long)getCurrentStateKeyDuration());
-}
-
-int64_t OringDurationTracker::getCurrentStateKeyDuration() const {
- auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
- if (it == mStateKeyDurationMap.end()) {
- return 0;
- } else {
- return it->second.mDuration;
- }
-}
-
-int64_t OringDurationTracker::getCurrentStateKeyFullBucketDuration() const {
- auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
- if (it == mStateKeyDurationMap.end()) {
- return 0;
- } else {
- return it->second.mDurationFullBucket;
- }
-}
-
-void OringDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
- HashableDimensionKey* stateValuesKey = mEventKey.getMutableStateValuesKey();
- for (size_t i = 0; i < stateValuesKey->getValues().size(); i++) {
- if (stateValuesKey->getValues()[i].mField.getTag() == atomId) {
- stateValuesKey->mutableValue(i)->mValue = newState.mValue;
- }
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
deleted file mode 100644
index 6eddee7da252..000000000000
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ /dev/null
@@ -1,93 +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.
- */
-
-#ifndef ORING_DURATION_TRACKER_H
-#define ORING_DURATION_TRACKER_H
-
-#include "DurationTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
-class OringDurationTracker : public DurationTracker {
-public:
- OringDurationTracker(const ConfigKey& key, const int64_t& id,
- const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, int64_t currentBucketStartNs,
- int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs,
- bool conditionSliced, bool fullLink,
- const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
-
- OringDurationTracker(const OringDurationTracker& tracker) = default;
-
- void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
- const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
- const bool stopAll) override;
- void noteStopAll(const int64_t eventTime) override;
-
- void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
- void onConditionChanged(bool condition, const int64_t timestamp) override;
-
- void onStateChanged(const int64_t timestamp, const int32_t atomId,
- const FieldValue& newState) override;
-
- bool flushCurrentBucket(
- const int64_t& eventTimeNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
- bool flushIfNeeded(
- int64_t timestampNs,
- std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
-
- int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
- const int64_t currentTimestamp) const override;
- void dumpStates(FILE* out, bool verbose) const override;
-
- int64_t getCurrentStateKeyDuration() const override;
-
- int64_t getCurrentStateKeyFullBucketDuration() const override;
-
- void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
-
-private:
- // We don't need to keep track of individual durations. The information that's needed is:
- // 1) which keys are started. We record the first start time.
- // 2) which keys are paused (started but condition was false)
- // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
- // it means everything has stopped, we then record the end time.
- std::unordered_map<HashableDimensionKey, int> mStarted;
- std::unordered_map<HashableDimensionKey, int> mPaused;
- int64_t mLastStartTime;
- std::unordered_map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
-
- // return true if we should not allow newKey to be tracked because we are above the threshold
- bool hitGuardRail(const HashableDimensionKey& newKey);
-
- FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
- FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
- FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
- FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
- FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
- FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // ORING_DURATION_TRACKER_H
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
deleted file mode 100644
index 39789cd86bb1..000000000000
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ /dev/null
@@ -1,1118 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "config_update_utils.h"
-
-#include "external/StatsPullerManager.h"
-#include "hash.h"
-#include "matchers/EventMatcherWizard.h"
-#include "metrics_manager_util.h"
-
-using google::protobuf::MessageLite;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate.
-// Returns whether the function was successful or not.
-bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- vector<UpdateStatus>& matchersToUpdate,
- vector<bool>& cycleTracker) {
- // Have already examined this matcher.
- if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) {
- return true;
- }
-
- const AtomMatcher& matcher = config.atom_matcher(matcherIdx);
- int64_t id = matcher.id();
- // Check if new matcher.
- const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
- if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
- matchersToUpdate[matcherIdx] = UPDATE_NEW;
- return true;
- }
-
- // This is an existing matcher. Check if it has changed.
- string serializedMatcher;
- if (!matcher.SerializeToString(&serializedMatcher)) {
- ALOGE("Unable to serialize matcher %lld", (long long)id);
- return false;
- }
- uint64_t newProtoHash = Hash64(serializedMatcher);
- if (newProtoHash != oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]->getProtoHash()) {
- matchersToUpdate[matcherIdx] = UPDATE_REPLACE;
- return true;
- }
-
- switch (matcher.contents_case()) {
- case AtomMatcher::ContentsCase::kSimpleAtomMatcher: {
- matchersToUpdate[matcherIdx] = UPDATE_PRESERVE;
- return true;
- }
- case AtomMatcher::ContentsCase::kCombination: {
- // Recurse to check if children have changed.
- cycleTracker[matcherIdx] = true;
- UpdateStatus status = UPDATE_PRESERVE;
- for (const int64_t childMatcherId : matcher.combination().matcher()) {
- const auto& childIt = newAtomMatchingTrackerMap.find(childMatcherId);
- if (childIt == newAtomMatchingTrackerMap.end()) {
- ALOGW("Matcher %lld not found in the config", (long long)childMatcherId);
- return false;
- }
- const int childIdx = childIt->second;
- if (cycleTracker[childIdx]) {
- ALOGE("Cycle detected in matcher config");
- return false;
- }
- if (!determineMatcherUpdateStatus(
- config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers,
- newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) {
- return false;
- }
-
- if (matchersToUpdate[childIdx] == UPDATE_REPLACE) {
- status = UPDATE_REPLACE;
- break;
- }
- }
- matchersToUpdate[matcherIdx] = status;
- cycleTracker[matcherIdx] = false;
- return true;
- }
- default: {
- ALOGE("Matcher \"%lld\" malformed", (long long)id);
- return false;
- }
- }
- return true;
-}
-
-bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
- set<int>& allTagIds,
- unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
- set<int64_t>& replacedMatchers) {
- const int atomMatcherCount = config.atom_matcher_size();
- vector<AtomMatcher> matcherProtos;
- matcherProtos.reserve(atomMatcherCount);
- newAtomMatchingTrackers.reserve(atomMatcherCount);
-
- // Maps matcher id to their position in the config. For fast lookup of dependencies.
- for (int i = 0; i < atomMatcherCount; i++) {
- const AtomMatcher& matcher = config.atom_matcher(i);
- if (newAtomMatchingTrackerMap.find(matcher.id()) != newAtomMatchingTrackerMap.end()) {
- ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id());
- return false;
- }
- newAtomMatchingTrackerMap[matcher.id()] = i;
- matcherProtos.push_back(matcher);
- }
-
- // For combination matchers, we need to determine if any children need to be updated.
- vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(atomMatcherCount, false);
- for (int i = 0; i < atomMatcherCount; i++) {
- if (!determineMatcherUpdateStatus(config, i, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
- matchersToUpdate, cycleTracker)) {
- return false;
- }
- }
-
- for (int i = 0; i < atomMatcherCount; i++) {
- const AtomMatcher& matcher = config.atom_matcher(i);
- const int64_t id = matcher.id();
- switch (matchersToUpdate[i]) {
- case UPDATE_PRESERVE: {
- const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
- if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
- ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it "
- "to be there",
- (long long)id);
- return false;
- }
- const sp<AtomMatchingTracker>& tracker =
- oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second];
- if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) {
- ALOGW("Config update failed for matcher %lld", (long long)id);
- return false;
- }
- newAtomMatchingTrackers.push_back(tracker);
- break;
- }
- case UPDATE_REPLACE:
- replacedMatchers.insert(id);
- [[fallthrough]]; // Intentionally fallthrough to create the new matcher.
- case UPDATE_NEW: {
- sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap);
- if (tracker == nullptr) {
- return false;
- }
- newAtomMatchingTrackers.push_back(tracker);
- break;
- }
- default: {
- ALOGE("Matcher \"%lld\" update state is unknown. This should never happen",
- (long long)id);
- return false;
- }
- }
- }
-
- std::fill(cycleTracker.begin(), cycleTracker.end(), false);
- for (auto& matcher : newAtomMatchingTrackers) {
- if (!matcher->init(matcherProtos, newAtomMatchingTrackers, newAtomMatchingTrackerMap,
- cycleTracker)) {
- return false;
- }
- // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
- const set<int>& tagIds = matcher->getAtomIds();
- allTagIds.insert(tagIds.begin(), tagIds.end());
- }
-
- return true;
-}
-
-// Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate.
-// Returns whether the function was successful or not.
-bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
- const unordered_map<int64_t, int>& oldConditionTrackerMap,
- const vector<sp<ConditionTracker>>& oldConditionTrackers,
- const unordered_map<int64_t, int>& newConditionTrackerMap,
- const set<int64_t>& replacedMatchers,
- vector<UpdateStatus>& conditionsToUpdate,
- vector<bool>& cycleTracker) {
- // Have already examined this condition.
- if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) {
- return true;
- }
-
- const Predicate& predicate = config.predicate(conditionIdx);
- int64_t id = predicate.id();
- // Check if new condition.
- const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
- if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
- conditionsToUpdate[conditionIdx] = UPDATE_NEW;
- return true;
- }
-
- // This is an existing condition. Check if it has changed.
- string serializedCondition;
- if (!predicate.SerializeToString(&serializedCondition)) {
- ALOGE("Unable to serialize matcher %lld", (long long)id);
- return false;
- }
- uint64_t newProtoHash = Hash64(serializedCondition);
- if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) {
- conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
- return true;
- }
-
- switch (predicate.contents_case()) {
- case Predicate::ContentsCase::kSimplePredicate: {
- // Need to check if any of the underlying matchers changed.
- const SimplePredicate& simplePredicate = predicate.simple_predicate();
- if (simplePredicate.has_start()) {
- if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) {
- conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
- return true;
- }
- }
- if (simplePredicate.has_stop()) {
- if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) {
- conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
- return true;
- }
- }
- if (simplePredicate.has_stop_all()) {
- if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) {
- conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
- return true;
- }
- }
- conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE;
- return true;
- }
- case Predicate::ContentsCase::kCombination: {
- // Need to recurse on the children to see if any of the child predicates changed.
- cycleTracker[conditionIdx] = true;
- UpdateStatus status = UPDATE_PRESERVE;
- for (const int64_t childPredicateId : predicate.combination().predicate()) {
- const auto& childIt = newConditionTrackerMap.find(childPredicateId);
- if (childIt == newConditionTrackerMap.end()) {
- ALOGW("Predicate %lld not found in the config", (long long)childPredicateId);
- return false;
- }
- const int childIdx = childIt->second;
- if (cycleTracker[childIdx]) {
- ALOGE("Cycle detected in predicate config");
- return false;
- }
- if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap,
- oldConditionTrackers, newConditionTrackerMap,
- replacedMatchers, conditionsToUpdate,
- cycleTracker)) {
- return false;
- }
-
- if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) {
- status = UPDATE_REPLACE;
- break;
- }
- }
- conditionsToUpdate[conditionIdx] = status;
- cycleTracker[conditionIdx] = false;
- return true;
- }
- default: {
- ALOGE("Predicate \"%lld\" malformed", (long long)id);
- return false;
- }
- }
-
- return true;
-}
-
-bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const set<int64_t>& replacedMatchers,
- const unordered_map<int64_t, int>& oldConditionTrackerMap,
- const vector<sp<ConditionTracker>>& oldConditionTrackers,
- unordered_map<int64_t, int>& newConditionTrackerMap,
- vector<sp<ConditionTracker>>& newConditionTrackers,
- unordered_map<int, vector<int>>& trackerToConditionMap,
- vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) {
- vector<Predicate> conditionProtos;
- const int conditionTrackerCount = config.predicate_size();
- conditionProtos.reserve(conditionTrackerCount);
- newConditionTrackers.reserve(conditionTrackerCount);
- conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
-
- for (int i = 0; i < conditionTrackerCount; i++) {
- const Predicate& condition = config.predicate(i);
- if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) {
- ALOGE("Duplicate Predicate found!");
- return false;
- }
- newConditionTrackerMap[condition.id()] = i;
- conditionProtos.push_back(condition);
- }
-
- vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(conditionTrackerCount, false);
- for (int i = 0; i < conditionTrackerCount; i++) {
- if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers,
- newConditionTrackerMap, replacedMatchers,
- conditionsToUpdate, cycleTracker)) {
- return false;
- }
- }
-
- // Update status has been determined for all conditions. Now perform the update.
- set<int> preservedConditions;
- for (int i = 0; i < conditionTrackerCount; i++) {
- const Predicate& predicate = config.predicate(i);
- const int64_t id = predicate.id();
- switch (conditionsToUpdate[i]) {
- case UPDATE_PRESERVE: {
- preservedConditions.insert(i);
- const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
- if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
- ALOGE("Could not find Predicate %lld in the previous config, but expected it "
- "to be there",
- (long long)id);
- return false;
- }
- const int oldIndex = oldConditionTrackerIt->second;
- newConditionTrackers.push_back(oldConditionTrackers[oldIndex]);
- break;
- }
- case UPDATE_REPLACE:
- replacedConditions.insert(id);
- [[fallthrough]]; // Intentionally fallthrough to create the new condition tracker.
- case UPDATE_NEW: {
- sp<ConditionTracker> tracker =
- createConditionTracker(key, predicate, i, atomMatchingTrackerMap);
- if (tracker == nullptr) {
- return false;
- }
- newConditionTrackers.push_back(tracker);
- break;
- }
- default: {
- ALOGE("Condition \"%lld\" update state is unknown. This should never happen",
- (long long)id);
- return false;
- }
- }
- }
-
- // Update indices of preserved predicates.
- for (const int conditionIndex : preservedConditions) {
- if (!newConditionTrackers[conditionIndex]->onConfigUpdated(
- conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap,
- newConditionTrackerMap)) {
- ALOGE("Failed to update condition %lld",
- (long long)newConditionTrackers[conditionIndex]->getConditionId());
- return false;
- }
- }
-
- std::fill(cycleTracker.begin(), cycleTracker.end(), false);
- for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) {
- const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex];
- // Calling init on preserved conditions is OK. It is needed to fill the condition cache.
- if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap,
- cycleTracker, conditionCache)) {
- return false;
- }
- for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
- vector<int>& conditionList = trackerToConditionMap[trackerIndex];
- conditionList.push_back(conditionIndex);
- }
- }
- return true;
-}
-
-bool updateStates(const StatsdConfig& config, const map<int64_t, uint64_t>& oldStateProtoHashes,
- unordered_map<int64_t, int>& stateAtomIdMap,
- unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- map<int64_t, uint64_t>& newStateProtoHashes, set<int64_t>& replacedStates) {
- // Share with metrics_manager_util.
- if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) {
- return false;
- }
-
- for (const auto& [stateId, stateHash] : oldStateProtoHashes) {
- const auto& it = newStateProtoHashes.find(stateId);
- if (it != newStateProtoHashes.end() && it->second != stateHash) {
- replacedStates.insert(stateId);
- }
- }
- return true;
-}
-// Returns true if any matchers in the metric activation were replaced.
-bool metricActivationDepsChange(const StatsdConfig& config,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const int64_t metricId, const set<int64_t>& replacedMatchers) {
- const auto& metricActivationIt = metricToActivationMap.find(metricId);
- if (metricActivationIt == metricToActivationMap.end()) {
- return false;
- }
- const MetricActivation& metricActivation = config.metric_activation(metricActivationIt->second);
- for (int i = 0; i < metricActivation.event_activation_size(); i++) {
- const EventActivation& activation = metricActivation.event_activation(i);
- if (replacedMatchers.find(activation.atom_matcher_id()) != replacedMatchers.end()) {
- return true;
- }
- if (activation.has_deactivation_atom_matcher_id()) {
- if (replacedMatchers.find(activation.deactivation_atom_matcher_id()) !=
- replacedMatchers.end()) {
- return true;
- }
- }
- }
- return false;
-}
-
-bool determineMetricUpdateStatus(
- const StatsdConfig& config, const MessageLite& metric, const int64_t metricId,
- const MetricType metricType, const set<int64_t>& matcherDependencies,
- const set<int64_t>& conditionDependencies,
- const ::google::protobuf::RepeatedField<int64_t>& stateDependencies,
- const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& conditionLinks,
- const unordered_map<int64_t, int>& oldMetricProducerMap,
- const vector<sp<MetricProducer>>& oldMetricProducers,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions,
- const set<int64_t>& replacedStates, UpdateStatus& updateStatus) {
- // Check if new metric
- const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
- if (oldMetricProducerIt == oldMetricProducerMap.end()) {
- updateStatus = UPDATE_NEW;
- return true;
- }
-
- // This is an existing metric, check if it has changed.
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) {
- return false;
- }
- const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
- if (oldMetricProducer->getMetricType() != metricType ||
- oldMetricProducer->getProtoHash() != metricHash) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
-
- // Take intersections of the matchers/predicates/states that the metric
- // depends on with those that have been replaced. If a metric depends on any
- // replaced component, it too must be replaced.
- set<int64_t> intersection;
- set_intersection(matcherDependencies.begin(), matcherDependencies.end(),
- replacedMatchers.begin(), replacedMatchers.end(),
- inserter(intersection, intersection.begin()));
- if (intersection.size() > 0) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
- set_intersection(conditionDependencies.begin(), conditionDependencies.end(),
- replacedConditions.begin(), replacedConditions.end(),
- inserter(intersection, intersection.begin()));
- if (intersection.size() > 0) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
- set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(),
- replacedStates.end(), inserter(intersection, intersection.begin()));
- if (intersection.size() > 0) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
-
- for (const auto& metricConditionLink : conditionLinks) {
- if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
- }
-
- if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
-
- updateStatus = UPDATE_PRESERVE;
- return true;
-}
-
-bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
- const unordered_map<int64_t, int>& oldMetricProducerMap,
- const vector<sp<MetricProducer>>& oldMetricProducers,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const set<int64_t>& replacedMatchers,
- const set<int64_t>& replacedConditions,
- const set<int64_t>& replacedStates,
- vector<UpdateStatus>& metricsToUpdate) {
- int metricIndex = 0;
- for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
- const CountMetric& metric = config.count_metric(i);
- set<int64_t> conditionDependencies;
- if (metric.has_condition()) {
- conditionDependencies.insert(metric.condition());
- }
- if (!determineMetricUpdateStatus(
- config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()},
- conditionDependencies, metric.slice_by_state(), metric.links(),
- oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, replacedStates,
- metricsToUpdate[metricIndex])) {
- return false;
- }
- }
- for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
- const DurationMetric& metric = config.duration_metric(i);
- set<int64_t> conditionDependencies({metric.what()});
- if (metric.has_condition()) {
- conditionDependencies.insert(metric.condition());
- }
- if (!determineMetricUpdateStatus(
- config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{},
- conditionDependencies, metric.slice_by_state(), metric.links(),
- oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, replacedStates,
- metricsToUpdate[metricIndex])) {
- return false;
- }
- }
- for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
- const EventMetric& metric = config.event_metric(i);
- set<int64_t> conditionDependencies;
- if (metric.has_condition()) {
- conditionDependencies.insert(metric.condition());
- }
- if (!determineMetricUpdateStatus(
- config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()},
- conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
- metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, replacedStates,
- metricsToUpdate[metricIndex])) {
- return false;
- }
- }
- for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
- const ValueMetric& metric = config.value_metric(i);
- set<int64_t> conditionDependencies;
- if (metric.has_condition()) {
- conditionDependencies.insert(metric.condition());
- }
- if (!determineMetricUpdateStatus(
- config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()},
- conditionDependencies, metric.slice_by_state(), metric.links(),
- oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, replacedStates,
- metricsToUpdate[metricIndex])) {
- return false;
- }
- }
- for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
- const GaugeMetric& metric = config.gauge_metric(i);
- set<int64_t> conditionDependencies;
- if (metric.has_condition()) {
- conditionDependencies.insert(metric.condition());
- }
- set<int64_t> matcherDependencies({metric.what()});
- if (metric.has_trigger_event()) {
- matcherDependencies.insert(metric.trigger_event());
- }
- if (!determineMetricUpdateStatus(
- config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies,
- conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
- metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, replacedStates,
- metricsToUpdate[metricIndex])) {
- return false;
- }
- }
- return true;
-}
-
-// Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers
-// and calls onConfigUpdated to update all indices.
-optional<sp<MetricProducer>> updateMetric(
- const StatsdConfig& config, const int configIndex, const int metricIndex,
- const int64_t metricId, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const sp<EventMatcherWizard>& matcherWizard,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& oldMetricProducerMap,
- const vector<sp<MetricProducer>>& oldMetricProducers,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
- if (oldMetricProducerIt == oldMetricProducerMap.end()) {
- ALOGE("Could not find Metric %lld in the previous config, but expected it "
- "to be there",
- (long long)metricId);
- return nullopt;
- }
- const int oldIndex = oldMetricProducerIt->second;
- sp<MetricProducer> producer = oldMetricProducers[oldIndex];
- if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
- matcherWizard, allConditionTrackers, conditionTrackerMap, wizard,
- metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
- activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- return nullopt;
- }
- return {producer};
-}
-
-bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const set<int64_t>& replacedMatchers,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const set<int64_t>& replacedConditions,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const vector<ConditionState>& initialConditionCache,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- const set<int64_t>& replacedStates,
- const unordered_map<int64_t, int>& oldMetricProducerMap,
- const vector<sp<MetricProducer>>& oldMetricProducers,
- unordered_map<int64_t, int>& newMetricProducerMap,
- vector<sp<MetricProducer>>& newMetricProducers,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- set<int64_t>& noReportMetricIds,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation, set<int64_t>& replacedMetrics) {
- sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
- sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
- const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
- config.event_metric_size() + config.gauge_metric_size() +
- config.value_metric_size();
- newMetricProducers.reserve(allMetricsCount);
-
- // Construct map from metric id to metric activation index. The map will be used to determine
- // the metric activation corresponding to a metric.
- unordered_map<int64_t, int> metricToActivationMap;
- for (int i = 0; i < config.metric_activation_size(); i++) {
- const MetricActivation& metricActivation = config.metric_activation(i);
- int64_t metricId = metricActivation.metric_id();
- if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
- ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
- return false;
- }
- metricToActivationMap.insert({metricId, i});
- }
-
- vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN);
- if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap, replacedMatchers,
- replacedConditions, replacedStates, metricsToUpdate)) {
- return false;
- }
-
- // Now, perform the update. Must iterate the metric types in the same order
- int metricIndex = 0;
- for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
- const CountMetric& metric = config.count_metric(i);
- newMetricProducerMap[metric.id()] = metricIndex;
- optional<sp<MetricProducer>> producer;
- switch (metricsToUpdate[metricIndex]) {
- case UPDATE_PRESERVE: {
- producer = updateMetric(
- config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- case UPDATE_REPLACE:
- replacedMetrics.insert(metric.id());
- [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
- case UPDATE_NEW: {
- producer = createCountMetricProducerAndUpdateMetadata(
- key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
- allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- default: {
- ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
- (long long)metric.id());
- return false;
- }
- }
- if (!producer) {
- return false;
- }
- newMetricProducers.push_back(producer.value());
- }
- for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
- const DurationMetric& metric = config.duration_metric(i);
- newMetricProducerMap[metric.id()] = metricIndex;
- optional<sp<MetricProducer>> producer;
- switch (metricsToUpdate[metricIndex]) {
- case UPDATE_PRESERVE: {
- producer = updateMetric(
- config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- case UPDATE_REPLACE:
- replacedMetrics.insert(metric.id());
- [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
- case UPDATE_NEW: {
- producer = createDurationMetricProducerAndUpdateMetadata(
- key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
- allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- default: {
- ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
- (long long)metric.id());
- return false;
- }
- }
- if (!producer) {
- return false;
- }
- newMetricProducers.push_back(producer.value());
- }
- for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
- const EventMetric& metric = config.event_metric(i);
- newMetricProducerMap[metric.id()] = metricIndex;
- optional<sp<MetricProducer>> producer;
- switch (metricsToUpdate[metricIndex]) {
- case UPDATE_PRESERVE: {
- producer = updateMetric(
- config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- case UPDATE_REPLACE:
- replacedMetrics.insert(metric.id());
- [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
- case UPDATE_NEW: {
- producer = createEventMetricProducerAndUpdateMetadata(
- key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
- newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
- initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- default: {
- ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
- (long long)metric.id());
- return false;
- }
- }
- if (!producer) {
- return false;
- }
- newMetricProducers.push_back(producer.value());
- }
-
- for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
- const ValueMetric& metric = config.value_metric(i);
- newMetricProducerMap[metric.id()] = metricIndex;
- optional<sp<MetricProducer>> producer;
- switch (metricsToUpdate[metricIndex]) {
- case UPDATE_PRESERVE: {
- producer = updateMetric(
- config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- case UPDATE_REPLACE:
- replacedMetrics.insert(metric.id());
- [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
- case UPDATE_NEW: {
- producer = createValueMetricProducerAndUpdateMetadata(
- key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
- stateAtomIdMap, allStateGroupMaps, metricToActivationMap,
- trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- default: {
- ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
- (long long)metric.id());
- return false;
- }
- }
- if (!producer) {
- return false;
- }
- newMetricProducers.push_back(producer.value());
- }
-
- for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
- const GaugeMetric& metric = config.gauge_metric(i);
- newMetricProducerMap[metric.id()] = metricIndex;
- optional<sp<MetricProducer>> producer;
- switch (metricsToUpdate[metricIndex]) {
- case UPDATE_PRESERVE: {
- producer = updateMetric(
- config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
- allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- break;
- }
- case UPDATE_REPLACE:
- replacedMetrics.insert(metric.id());
- [[fallthrough]]; // Intentionally fallthrough to create the new metric producer.
- case UPDATE_NEW: {
- producer = createGaugeMetricProducerAndUpdateMetadata(
- key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
- allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
- metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation);
- break;
- }
- default: {
- ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
- (long long)metric.id());
- return false;
- }
- }
- if (!producer) {
- return false;
- }
- newMetricProducers.push_back(producer.value());
- }
-
- for (int i = 0; i < config.no_report_metric_size(); ++i) {
- const int64_t noReportMetric = config.no_report_metric(i);
- if (newMetricProducerMap.find(noReportMetric) == newMetricProducerMap.end()) {
- ALOGW("no_report_metric %" PRId64 " not exist", noReportMetric);
- return false;
- }
- noReportMetricIds.insert(noReportMetric);
- }
- const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
- config.whitelisted_atom_ids().end());
- for (int i = 0; i < allMetricsCount; i++) {
- sp<MetricProducer> producer = newMetricProducers[i];
- // Register metrics to StateTrackers
- for (int atomId : producer->getSlicedStateAtoms()) {
- // Register listener for atoms that use allowed_log_sources.
- // Using atoms allowed from any uid as a sliced state atom is not allowed.
- // Redo this check for all metrics in case the atoms allowed from any uid changed.
- if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) {
- return false;
- // Preserved metrics should've already registered.`
- } else if (metricsToUpdate[i] != UPDATE_PRESERVE) {
- StateManager::getInstance().registerListener(atomId, producer);
- }
- }
- }
-
- // Init new/replaced metrics.
- for (size_t i = 0; i < newMetricProducers.size(); i++) {
- if (metricsToUpdate[i] == UPDATE_REPLACE || metricsToUpdate[i] == UPDATE_NEW) {
- newMetricProducers[i]->prepareFirstBucket();
- }
- }
- return true;
-}
-
-bool determineAlertUpdateStatus(const Alert& alert,
- const unordered_map<int64_t, int>& oldAlertTrackerMap,
- const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
- const set<int64_t>& replacedMetrics, UpdateStatus& updateStatus) {
- // Check if new alert.
- const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id());
- if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) {
- updateStatus = UPDATE_NEW;
- return true;
- }
-
- // This is an existing alert, check if it has changed.
- string serializedAlert;
- if (!alert.SerializeToString(&serializedAlert)) {
- ALOGW("Unable to serialize alert %lld", (long long)alert.id());
- return false;
- }
- uint64_t newProtoHash = Hash64(serializedAlert);
- const auto [success, oldProtoHash] =
- oldAnomalyTrackers[oldAnomalyTrackerIt->second]->getProtoHash();
- if (!success) {
- return false;
- }
- if (newProtoHash != oldProtoHash) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
-
- // Check if the metric this alert relies on has changed.
- if (replacedMetrics.find(alert.metric_id()) != replacedMetrics.end()) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
-
- updateStatus = UPDATE_PRESERVE;
- return true;
-}
-
-bool updateAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap,
- const set<int64_t>& replacedMetrics,
- const unordered_map<int64_t, int>& oldAlertTrackerMap,
- const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int64_t, int>& newAlertTrackerMap,
- vector<sp<AnomalyTracker>>& newAnomalyTrackers) {
- int alertCount = config.alert_size();
- vector<UpdateStatus> alertUpdateStatuses(alertCount);
- for (int i = 0; i < alertCount; i++) {
- if (!determineAlertUpdateStatus(config.alert(i), oldAlertTrackerMap, oldAnomalyTrackers,
- replacedMetrics, alertUpdateStatuses[i])) {
- return false;
- }
- }
-
- for (int i = 0; i < alertCount; i++) {
- const Alert& alert = config.alert(i);
- newAlertTrackerMap[alert.id()] = newAnomalyTrackers.size();
- switch (alertUpdateStatuses[i]) {
- case UPDATE_PRESERVE: {
- // Find the alert and update it.
- const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id());
- if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) {
- ALOGW("Could not find AnomalyTracker %lld in the previous config, but "
- "expected it to be there",
- (long long)alert.id());
- return false;
- }
- sp<AnomalyTracker> anomalyTracker = oldAnomalyTrackers[oldAnomalyTrackerIt->second];
- anomalyTracker->onConfigUpdated();
- // Add the alert to the relevant metric.
- const auto& metricProducerIt = metricProducerMap.find(alert.metric_id());
- if (metricProducerIt == metricProducerMap.end()) {
- ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
- (long long)alert.metric_id());
- return false;
- }
- allMetricProducers[metricProducerIt->second]->addAnomalyTracker(anomalyTracker);
- newAnomalyTrackers.push_back(anomalyTracker);
- break;
- }
- case UPDATE_REPLACE:
- case UPDATE_NEW: {
- optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker(
- alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers);
- if (!anomalyTracker) {
- return false;
- }
- newAnomalyTrackers.push_back(anomalyTracker.value());
- break;
- }
- default: {
- ALOGE("Alert \"%lld\" update state is unknown. This should never happen",
- (long long)alert.id());
- return false;
- }
- }
- }
- if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, newAlertTrackerMap,
- newAnomalyTrackers)) {
- return false;
- }
- return true;
-}
-
-bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs,
- const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const vector<sp<ConditionTracker>>& oldConditionTrackers,
- const unordered_map<int64_t, int>& oldConditionTrackerMap,
- const vector<sp<MetricProducer>>& oldMetricProducers,
- const unordered_map<int64_t, int>& oldMetricProducerMap,
- const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
- const unordered_map<int64_t, int>& oldAlertTrackerMap,
- const map<int64_t, uint64_t>& oldStateProtoHashes, set<int>& allTagIds,
- vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
- unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- vector<sp<ConditionTracker>>& newConditionTrackers,
- unordered_map<int64_t, int>& newConditionTrackerMap,
- vector<sp<MetricProducer>>& newMetricProducers,
- unordered_map<int64_t, int>& newMetricProducerMap,
- vector<sp<AnomalyTracker>>& newAnomalyTrackers,
- unordered_map<int64_t, int>& newAlertTrackerMap,
- vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& trackerToConditionMap,
- unordered_map<int, vector<int>>& activationTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationTrackerToMetricMap,
- vector<int>& metricsWithActivation,
- map<int64_t, uint64_t>& newStateProtoHashes,
- set<int64_t>& noReportMetricIds) {
- set<int64_t> replacedMatchers;
- set<int64_t> replacedConditions;
- set<int64_t> replacedStates;
- set<int64_t> replacedMetrics;
- vector<ConditionState> conditionCache;
- unordered_map<int64_t, int> stateAtomIdMap;
- unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
-
- if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap,
- newAtomMatchingTrackers, replacedMatchers)) {
- ALOGE("updateAtomMatchingTrackers failed");
- return false;
- }
-
- if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers,
- oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap,
- newConditionTrackers, trackerToConditionMap, conditionCache,
- replacedConditions)) {
- ALOGE("updateConditions failed");
- return false;
- }
-
- if (!updateStates(config, oldStateProtoHashes, stateAtomIdMap, allStateGroupMaps,
- newStateProtoHashes, replacedStates)) {
- ALOGE("updateStates failed");
- return false;
- }
- if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager,
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
- newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
- newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps,
- replacedStates, oldMetricProducerMap, oldMetricProducers,
- newMetricProducerMap, newMetricProducers, conditionToMetricMap,
- trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap,
- deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics)) {
- ALOGE("updateMetrics failed");
- return false;
- }
-
- if (!updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap,
- oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers,
- newAlertTrackerMap, newAnomalyTrackers)) {
- ALOGE("updateAlerts failed");
- return false;
- }
-
- // Alarms do not have any state, so we can reuse the initialization logic.
- if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
- newPeriodicAlarmTrackers)) {
- ALOGE("initAlarms failed");
- return false;
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
deleted file mode 100644
index 8e2be6899699..000000000000
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#pragma once
-
-#include <vector>
-
-#include "anomaly/AlarmMonitor.h"
-#include "anomaly/AlarmTracker.h"
-#include "condition/ConditionTracker.h"
-#include "external/StatsPullerManager.h"
-#include "matchers/AtomMatchingTracker.h"
-#include "metrics/MetricProducer.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Helper functions for MetricsManager to update itself from a new StatsdConfig.
-// *Note*: only updateStatsdConfig() should be called from outside this file.
-// All other functions are intermediate steps, created to make unit testing easier.
-
-// Possible update states for a component. PRESERVE means we should keep the existing one.
-// REPLACE means we should create a new one because the existing one changed
-// NEW means we should create a new one because one does not currently exist.
-enum UpdateStatus {
- UPDATE_UNKNOWN = 0,
- UPDATE_PRESERVE = 1,
- UPDATE_REPLACE = 2,
- UPDATE_NEW = 3,
-};
-
-// Recursive function to determine if a matcher needs to be updated.
-// input:
-// [config]: the input StatsdConfig
-// [matcherIdx]: the index of the current matcher to be updated
-// [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager
-// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
-// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig
-// output:
-// [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will
-// be updated from UPDATE_UNKNOWN after this call.
-// [cycleTracker]: intermediate param used during recursion.
-// Returns whether the function was successful or not.
-bool determineMatcherUpdateStatus(
- const StatsdConfig& config, const int matcherIdx,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker);
-
-// Updates the AtomMatchingTrackers.
-// input:
-// [config]: the input StatsdConfig
-// [oldAtomMatchingTrackerMap]: existing matcher id to index mapping
-// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
-// output:
-// [allTagIds]: contains the set of all interesting tag ids to this config.
-// [newAtomMatchingTrackerMap]: new matcher id to index mapping
-// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers
-// [replacedMatchers]: set of matcher ids that changed and have been replaced
-bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
- std::set<int>& allTagIds,
- std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
- std::set<int64_t>& replacedMatchers);
-
-// Recursive function to determine if a condition needs to be updated.
-// input:
-// [config]: the input StatsdConfig
-// [conditionIdx]: the index of the current condition to be updated
-// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager
-// [oldConditionTrackers]: stores the existing ConditionTrackers
-// [newConditionTrackerMap]: condition id to index mapping in the input StatsdConfig
-// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced
-// output:
-// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will
-// be updated from UPDATE_UNKNOWN after this call.
-// [cycleTracker]: intermediate param used during recursion.
-// Returns whether the function was successful or not.
-bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
- const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
- const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
- const std::unordered_map<int64_t, int>& newConditionTrackerMap,
- const std::set<int64_t>& replacedMatchers,
- std::vector<UpdateStatus>& conditionsToUpdate,
- std::vector<bool>& cycleTracker);
-
-// Updates ConditionTrackers
-// input:
-// [config]: the input config
-// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
-// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced
-// [oldConditionTrackerMap]: existing matcher id to index mapping
-// [oldConditionTrackers]: stores the existing ConditionTrackers
-// output:
-// [newConditionTrackerMap]: new condition id to index mapping
-// [newConditionTrackers]: stores the sp to all the ConditionTrackers
-// [trackerToConditionMap]: contains the mapping from the index of an atom matcher
-// to indices of condition trackers that use the matcher
-// [conditionCache]: stores the current conditions for each ConditionTracker
-// [replacedConditions]: set of condition ids that have changed and have been replaced
-bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const std::set<int64_t>& replacedMatchers,
- const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
- const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
- std::unordered_map<int64_t, int>& newConditionTrackerMap,
- std::vector<sp<ConditionTracker>>& newConditionTrackers,
- std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- std::vector<ConditionState>& conditionCache,
- std::set<int64_t>& replacedConditions);
-
-bool updateStates(const StatsdConfig& config,
- const std::map<int64_t, uint64_t>& oldStateProtoHashes,
- std::unordered_map<int64_t, int>& stateAtomIdMap,
- std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
- std::map<int64_t, uint64_t>& newStateProtoHashes,
- std::set<int64_t>& replacedStates);
-
-// Function to determine the update status (preserve/replace/new) of all metrics in the config.
-// [config]: the input StatsdConfig
-// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager
-// [oldMetricProducers]: stores the existing MetricProducers
-// [metricToActivationMap]: map from metric id to metric activation index
-// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced
-// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced
-// [replacedStates]: set of replaced state ids. metrics using these states must be replaced
-// output:
-// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN
-// Returns whether the function was successful or not.
-bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
- const unordered_map<int64_t, int>& oldMetricProducerMap,
- const vector<sp<MetricProducer>>& oldMetricProducers,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const set<int64_t>& replacedMatchers,
- const set<int64_t>& replacedConditions,
- const set<int64_t>& replacedStates,
- vector<UpdateStatus>& metricsToUpdate);
-
-// Update MetricProducers.
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input config
-// [timeBaseNs]: start time base for all metrics
-// [currentTimeNs]: time of the config update
-// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
-// [replacedMatchers]: ids of replaced matchers. Metrics depending on these must also be replaced
-// [allAtomMatchingTrackers]: stores the sp of the atom matchers.
-// [conditionTrackerMap]: condition name to index mapping
-// [replacedConditions]: set of condition ids that have changed and have been replaced
-// [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
-// the list of MetricProducer index
-// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
-bool updateMetrics(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const std::set<int64_t>& replacedMatchers,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const std::set<int64_t>& replacedConditions,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::vector<ConditionState>& initialConditionCache,
- const std::unordered_map<int64_t, int>& stateAtomIdMap,
- const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
- const std::set<int64_t>& replacedStates,
- const std::unordered_map<int64_t, int>& oldMetricProducerMap,
- const std::vector<sp<MetricProducer>>& oldMetricProducers,
- std::unordered_map<int64_t, int>& newMetricProducerMap,
- std::vector<sp<MetricProducer>>& newMetricProducers,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::set<int64_t>& noReportMetricIds,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation, std::set<int64_t>& replacedMetrics);
-
-// Function to determine the update status (preserve/replace/new) of an alert.
-// [alert]: the input Alert
-// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager
-// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
-// [replacedMetrics]: set of replaced metric ids. alerts using these metrics must be replaced
-// output:
-// [updateStatus]: update status of the alert. Will be changed from UPDATE_UNKNOWN
-// Returns whether the function was successful or not.
-bool determineAlertUpdateStatus(const Alert& alert,
- const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
- const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
- const std::set<int64_t>& replacedMetrics,
- UpdateStatus& updateStatus);
-
-// Update MetricProducers.
-// input:
-// [config]: the input config
-// [metricProducerMap]: metric id to index mapping in the new config
-// [replacedMetrics]: set of metric ids that have changed and were replaced
-// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager.
-// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
-// [anomalyAlarmMonitor]: AlarmMonitor used for duration metric anomaly detection
-// [allMetricProducers]: stores the sp of the metric producers, AnomalyTrackers need to be added.
-// [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:
-// [newAlertTrackerMap]: mapping of alert id to index in the new config
-// [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created.
-bool updateAlerts(const StatsdConfig& config,
- const std::unordered_map<int64_t, int>& metricProducerMap,
- const std::set<int64_t>& replacedMetrics,
- const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
- const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- std::vector<sp<MetricProducer>>& allMetricProducers,
- std::unordered_map<int64_t, int>& newAlertTrackerMap,
- std::vector<sp<AnomalyTracker>>& newAnomalyTrackers);
-
-// Updates the existing MetricsManager from a new StatsdConfig.
-// Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs,
- const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
- const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
- const std::vector<sp<MetricProducer>>& oldMetricProducers,
- const std::unordered_map<int64_t, int>& oldMetricProducerMap,
- const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
- const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
- const std::map<int64_t, uint64_t>& oldStateProtoHashes,
- std::set<int>& allTagIds,
- std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
- std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- std::vector<sp<ConditionTracker>>& newConditionTrackers,
- std::unordered_map<int64_t, int>& newConditionTrackerMap,
- std::vector<sp<MetricProducer>>& newMetricProducers,
- std::unordered_map<int64_t, int>& newMetricProducerMap,
- std::vector<sp<AnomalyTracker>>& newAlertTrackers,
- std::unordered_map<int64_t, int>& newAlertTrackerMap,
- std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap,
- std::vector<int>& metricsWithActivation,
- std::map<int64_t, uint64_t>& newStateProtoHashes,
- std::set<int64_t>& noReportMetricIds);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
deleted file mode 100644
index 4474df4346cf..000000000000
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ /dev/null
@@ -1,1220 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "metrics_manager_util.h"
-
-#include <inttypes.h>
-
-#include "FieldValue.h"
-#include "condition/CombinationConditionTracker.h"
-#include "condition/SimpleConditionTracker.h"
-#include "external/StatsPullerManager.h"
-#include "hash.h"
-#include "matchers/CombinationAtomMatchingTracker.h"
-#include "matchers/EventMatcherWizard.h"
-#include "matchers/SimpleAtomMatchingTracker.h"
-#include "metrics/CountMetricProducer.h"
-#include "metrics/DurationMetricProducer.h"
-#include "metrics/EventMetricProducer.h"
-#include "metrics/GaugeMetricProducer.h"
-#include "metrics/MetricProducer.h"
-#include "metrics/ValueMetricProducer.h"
-#include "state/StateManager.h"
-#include "stats_util.h"
-
-using google::protobuf::MessageLite;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-bool hasLeafNode(const FieldMatcher& matcher) {
- if (!matcher.has_field()) {
- return false;
- }
- for (int i = 0; i < matcher.child_size(); ++i) {
- if (hasLeafNode(matcher.child(i))) {
- return true;
- }
- }
- return true;
-}
-
-} // namespace
-
-sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
- const sp<UidMap>& uidMap) {
- string serializedMatcher;
- if (!logMatcher.SerializeToString(&serializedMatcher)) {
- ALOGE("Unable to serialize matcher %lld", (long long)logMatcher.id());
- return nullptr;
- }
- uint64_t protoHash = Hash64(serializedMatcher);
- switch (logMatcher.contents_case()) {
- case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
- return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash,
- logMatcher.simple_atom_matcher(), uidMap);
- case AtomMatcher::ContentsCase::kCombination:
- return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash);
- default:
- ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
- return nullptr;
- }
-}
-
-sp<ConditionTracker> createConditionTracker(
- const ConfigKey& key, const Predicate& predicate, const int index,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
- string serializedPredicate;
- if (!predicate.SerializeToString(&serializedPredicate)) {
- ALOGE("Unable to serialize predicate %lld", (long long)predicate.id());
- return nullptr;
- }
- uint64_t protoHash = Hash64(serializedPredicate);
- switch (predicate.contents_case()) {
- case Predicate::ContentsCase::kSimplePredicate: {
- return new SimpleConditionTracker(key, predicate.id(), protoHash, index,
- predicate.simple_predicate(), atomMatchingTrackerMap);
- }
- case Predicate::ContentsCase::kCombination: {
- return new CombinationConditionTracker(predicate.id(), index, protoHash);
- }
- default:
- ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id());
- return nullptr;
- }
-}
-
-bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, const int64_t id,
- const unordered_map<int64_t, int>& metricToActivationMap,
- uint64_t& metricHash) {
- string serializedMetric;
- if (!metric.SerializeToString(&serializedMetric)) {
- ALOGE("Unable to serialize metric %lld", (long long)id);
- return false;
- }
- metricHash = Hash64(serializedMetric);
-
- // Combine with activation hash, if applicable
- const auto& metricActivationIt = metricToActivationMap.find(id);
- if (metricActivationIt != metricToActivationMap.end()) {
- string serializedActivation;
- const MetricActivation& activation = config.metric_activation(metricActivationIt->second);
- if (!activation.SerializeToString(&serializedActivation)) {
- ALOGE("Unable to serialize metric activation for metric %lld", (long long)id);
- return false;
- }
- metricHash = Hash64(to_string(metricHash).append(to_string(Hash64(serializedActivation))));
- }
- return true;
-}
-
-bool handleMetricWithAtomMatchingTrackers(
- const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- unordered_map<int, vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
- auto logTrackerIt = atomMatchingTrackerMap.find(matcherId);
- if (logTrackerIt == atomMatchingTrackerMap.end()) {
- ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)matcherId);
- return false;
- }
- if (enforceOneAtom && allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) {
- ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
- "the \"what\" can only be about one atom type. trigger_event matchers can also only "
- "be about one atom type.",
- (long long)matcherId);
- return false;
- }
- logTrackerIndex = logTrackerIt->second;
- auto& metric_list = trackerToMetricMap[logTrackerIndex];
- metric_list.push_back(metricIndex);
- return true;
-}
-
-bool handleMetricWithConditions(
- const int64_t condition, const int metricIndex,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
- links,
- const vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
- unordered_map<int, vector<int>>& conditionToMetricMap) {
- auto condition_it = conditionTrackerMap.find(condition);
- if (condition_it == conditionTrackerMap.end()) {
- ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
- return false;
- }
-
- for (const auto& link : links) {
- auto it = conditionTrackerMap.find(link.condition());
- if (it == conditionTrackerMap.end()) {
- ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition());
- return false;
- }
- }
- conditionIndex = condition_it->second;
-
- // will create new vector if not exist before.
- auto& metricList = conditionToMetricMap[condition_it->second];
- metricList.push_back(metricIndex);
- 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;
-}
-
-bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
- const vector<Matcher>& dimensionsInWhat) {
- vector<Matcher> stateMatchers;
- translateFieldMatcher(stateMatcher, &stateMatchers);
-
- return subsetDimensions(stateMatchers, dimensionsInWhat);
-}
-
-// 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.
-// Returns false if there are errors.
-bool handleMetricActivation(
- const StatsdConfig& config, const int64_t metricId, const int metricIndex,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation,
- unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
- unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
- // Check if metric has an associated activation
- auto itr = metricToActivationMap.find(metricId);
- if (itr == metricToActivationMap.end()) {
- return true;
- }
-
- int activationIndex = itr->second;
- const MetricActivation& metricActivation = config.metric_activation(activationIndex);
-
- for (int i = 0; i < metricActivation.event_activation_size(); i++) {
- const EventActivation& activation = metricActivation.event_activation(i);
-
- auto itr = atomMatchingTrackerMap.find(activation.atom_matcher_id());
- if (itr == atomMatchingTrackerMap.end()) {
- ALOGE("Atom matcher not found for event activation.");
- return false;
- }
-
- ActivationType activationType = (activation.has_activation_type())
- ? activation.activation_type()
- : metricActivation.activation_type();
- std::shared_ptr<Activation> activationWrapper =
- std::make_shared<Activation>(activationType, activation.ttl_seconds() * NS_PER_SEC);
-
- int atomMatcherIndex = itr->second;
- activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
- eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
-
- if (activation.has_deactivation_atom_matcher_id()) {
- itr = atomMatchingTrackerMap.find(activation.deactivation_atom_matcher_id());
- if (itr == atomMatchingTrackerMap.end()) {
- ALOGE("Atom matcher not found for event deactivation.");
- return false;
- }
- int deactivationAtomMatcherIndex = itr->second;
- deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
- eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
- }
- }
-
- metricsWithActivation.push_back(metricIndex);
- return true;
-}
-
-// Validates a metricActivation and populates state.
-// Fills the new event activation/deactivation maps, preserving the existing activations
-// Returns false if there are errors.
-bool handleMetricActivationOnConfigUpdate(
- const StatsdConfig& config, const int64_t metricId, const int metricIndex,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation,
- unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
- unordered_map<int, vector<shared_ptr<Activation>>>& newEventDeactivationMap) {
- // Check if metric has an associated activation.
- const auto& itr = metricToActivationMap.find(metricId);
- if (itr == metricToActivationMap.end()) {
- return true;
- }
-
- int activationIndex = itr->second;
- const MetricActivation& metricActivation = config.metric_activation(activationIndex);
-
- for (int i = 0; i < metricActivation.event_activation_size(); i++) {
- const int64_t activationMatcherId = metricActivation.event_activation(i).atom_matcher_id();
-
- const auto& newActivationIt = newAtomMatchingTrackerMap.find(activationMatcherId);
- if (newActivationIt == newAtomMatchingTrackerMap.end()) {
- ALOGE("Atom matcher not found in new config for event activation.");
- return false;
- }
- int newActivationMatcherIndex = newActivationIt->second;
-
- // Find the old activation struct and copy it over.
- const auto& oldActivationIt = oldAtomMatchingTrackerMap.find(activationMatcherId);
- if (oldActivationIt == oldAtomMatchingTrackerMap.end()) {
- ALOGE("Atom matcher not found in existing config for event activation.");
- return false;
- }
- int oldActivationMatcherIndex = oldActivationIt->second;
- const auto& oldEventActivationIt = oldEventActivationMap.find(oldActivationMatcherIndex);
- if (oldEventActivationIt == oldEventActivationMap.end()) {
- ALOGE("Could not find existing event activation to update");
- return false;
- }
- newEventActivationMap.emplace(newActivationMatcherIndex, oldEventActivationIt->second);
- activationAtomTrackerToMetricMap[newActivationMatcherIndex].push_back(metricIndex);
-
- if (metricActivation.event_activation(i).has_deactivation_atom_matcher_id()) {
- const int64_t deactivationMatcherId =
- metricActivation.event_activation(i).deactivation_atom_matcher_id();
- const auto& newDeactivationIt = newAtomMatchingTrackerMap.find(deactivationMatcherId);
- if (newDeactivationIt == newAtomMatchingTrackerMap.end()) {
- ALOGE("Deactivation atom matcher not found in new config for event activation.");
- return false;
- }
- int newDeactivationMatcherIndex = newDeactivationIt->second;
- newEventDeactivationMap[newDeactivationMatcherIndex].push_back(
- oldEventActivationIt->second);
- deactivationAtomTrackerToMetricMap[newDeactivationMatcherIndex].push_back(metricIndex);
- }
- }
-
- metricsWithActivation.push_back(metricIndex);
- return true;
-}
-
-optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!metric.has_id() || !metric.has_what()) {
- ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
- return nullopt;
- }
- int trackerIndex;
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return nullopt;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, conditionIndex,
- conditionToMetricMap)) {
- return nullopt;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return nullopt;
- }
- }
-
- 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 nullopt;
- }
- } else {
- if (metric.state_link_size() > 0) {
- ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
- return nullopt;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
- atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- eventActivationMap, eventDeactivationMap)) {
- return nullopt;
- }
-
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
- return nullopt;
- }
-
- return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
- eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
-}
-
-optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!metric.has_id() || !metric.has_what()) {
- ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"",
- (long long)metric.id());
- return nullopt;
- }
- const auto& what_it = conditionTrackerMap.find(metric.what());
- if (what_it == conditionTrackerMap.end()) {
- ALOGE("DurationMetric's \"what\" is not present in the condition trackers");
- return nullopt;
- }
-
- const Predicate& durationWhat = config.predicate(what_it->second);
- if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
- ALOGE("DurationMetric's \"what\" must be a simple condition");
- return nullopt;
- }
-
- const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
- bool nesting = simplePredicate.count_nesting();
-
- int startIndex = -1, stopIndex = -1, stopAllIndex = -1;
- if (!simplePredicate.has_start() ||
- !handleMetricWithAtomMatchingTrackers(
- simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) {
- ALOGE("Duration metrics must specify a valid start event matcher");
- return nullopt;
- }
-
- if (simplePredicate.has_stop() &&
- !handleMetricWithAtomMatchingTrackers(
- simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) {
- return nullopt;
- }
-
- if (simplePredicate.has_stop_all() &&
- !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, stopAllIndex)) {
- return nullopt;
- }
-
- FieldMatcher internalDimensions = simplePredicate.dimensions();
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, conditionIndex,
- conditionToMetricMap)) {
- return nullopt;
- }
- } else if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return nullopt;
- }
-
- std::vector<int> slicedStateAtoms;
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
- if (metric.slice_by_state_size() > 0) {
- if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
- ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
- return nullopt;
- }
- if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
- allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
- return nullopt;
- }
- } else if (metric.state_link_size() > 0) {
- ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
- return nullopt;
- }
-
- // Check that all metric state links are a subset of dimensions_in_what fields.
- std::vector<Matcher> dimensionsInWhat;
- translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
- for (const auto& stateLink : metric.state_link()) {
- if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
- return nullopt;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
- atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- eventActivationMap, eventDeactivationMap)) {
- return nullopt;
- }
-
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
- return nullopt;
- }
-
- return {new DurationMetricProducer(
- key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex,
- nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs,
- eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
-}
-
-optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const EventMetric& metric, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!metric.has_id() || !metric.has_what()) {
- ALOGE("cannot find the metric name or what in config");
- return nullopt;
- }
- int trackerIndex;
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return nullopt;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, conditionIndex,
- conditionToMetricMap)) {
- return nullopt;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return nullopt;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
- atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- eventActivationMap, eventDeactivationMap);
- if (!success) return nullptr;
-
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
- return nullopt;
- }
-
- return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, timeBaseNs, eventActivationMap,
- eventDeactivationMap)};
-}
-
-optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const ValueMetric& metric, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const sp<EventMatcherWizard>& matcherWizard,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!metric.has_id() || !metric.has_what()) {
- ALOGE("cannot find metric id or \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
- return nullopt;
- }
- if (!metric.has_value_field()) {
- ALOGE("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
- return nullopt;
- }
- std::vector<Matcher> fieldMatchers;
- translateFieldMatcher(metric.value_field(), &fieldMatchers);
- if (fieldMatchers.size() < 1) {
- ALOGE("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
- return nullopt;
- }
-
- int trackerIndex;
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return nullopt;
- }
-
- sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
- // If it is pulled atom, it should be simple matcher with one tagId.
- if (atomMatcher->getAtomIds().size() != 1) {
- return nullopt;
- }
- int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, conditionIndex,
- conditionToMetricMap)) {
- return nullopt;
- }
- } else if (metric.links_size() > 0) {
- ALOGE("metrics has a MetricConditionLink but doesn't have a condition");
- return nullopt;
- }
-
- 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 nullopt;
- }
- } else if (metric.state_link_size() > 0) {
- ALOGE("ValueMetric has a MetricStateLink but doesn't have a sliced state");
- return nullopt;
- }
-
- // Check that all metric state links are a subset of dimensions_in_what fields.
- std::vector<Matcher> dimensionsInWhat;
- translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
- for (const auto& stateLink : metric.state_link()) {
- if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
- return nullopt;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
- atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- eventActivationMap, eventDeactivationMap)) {
- return nullopt;
- }
-
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
- return nullopt;
- }
-
- return {new ValueMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, trackerIndex, matcherWizard, pullTagId, timeBaseNs,
- currentTimeNs, pullerManager, eventActivationMap,
- eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
-}
-
-optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const GaugeMetric& metric, const int metricIndex,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const sp<EventMatcherWizard>& matcherWizard,
- const unordered_map<int64_t, int>& metricToActivationMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- if (!metric.has_id() || !metric.has_what()) {
- ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
- return nullopt;
- }
-
- if ((!metric.gauge_fields_filter().has_include_all() ||
- (metric.gauge_fields_filter().include_all() == false)) &&
- !hasLeafNode(metric.gauge_fields_filter().fields())) {
- ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
- return nullopt;
- }
- if ((metric.gauge_fields_filter().has_include_all() &&
- metric.gauge_fields_filter().include_all() == true) &&
- hasLeafNode(metric.gauge_fields_filter().fields())) {
- ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
- return nullopt;
- }
-
- int trackerIndex;
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return nullopt;
- }
-
- sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
- // For GaugeMetric atom, it should be simple matcher with one tagId.
- if (atomMatcher->getAtomIds().size() != 1) {
- return nullopt;
- }
- int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
- int triggerTrackerIndex;
- int triggerAtomId = -1;
- if (metric.has_trigger_event()) {
- if (pullTagId == -1) {
- ALOGW("Pull atom not specified for trigger");
- return nullopt;
- }
- // trigger_event should be used with FIRST_N_SAMPLES
- if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
- ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES");
- return nullopt;
- }
- if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
- /*enforceOneAtom=*/true, allAtomMatchingTrackers,
- atomMatchingTrackerMap, trackerToMetricMap,
- triggerTrackerIndex)) {
- return nullopt;
- }
- sp<AtomMatchingTracker> triggerAtomMatcher =
- allAtomMatchingTrackers.at(triggerTrackerIndex);
- triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
- }
-
- if (!metric.has_trigger_event() && pullTagId != -1 &&
- metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
- ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
- return nullopt;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
- metric.links(), allConditionTrackers, conditionIndex,
- conditionToMetricMap)) {
- return nullopt;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return nullopt;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
- atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation,
- eventActivationMap, eventDeactivationMap)) {
- return nullopt;
- }
-
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
- return nullopt;
- }
-
- return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, trackerIndex, matcherWizard, pullTagId,
- triggerAtomId, atomTagId, timeBaseNs, currentTimeNs,
- pullerManager, eventActivationMap, eventDeactivationMap)};
-}
-
-optional<sp<AnomalyTracker>> createAnomalyTracker(
- const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const unordered_map<int64_t, int>& metricProducerMap,
- vector<sp<MetricProducer>>& allMetricProducers) {
- const auto& itr = metricProducerMap.find(alert.metric_id());
- if (itr == metricProducerMap.end()) {
- ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
- (long long)alert.metric_id());
- return nullopt;
- }
- if (!alert.has_trigger_if_sum_gt()) {
- ALOGW("invalid alert: missing threshold");
- return nullopt;
- }
- if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
- ALOGW("invalid alert: threshold=%f num_buckets= %d", alert.trigger_if_sum_gt(),
- alert.num_buckets());
- return nullopt;
- }
- const int metricIndex = itr->second;
- sp<MetricProducer> metric = allMetricProducers[metricIndex];
- sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
- if (anomalyTracker == nullptr) {
- // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
- return nullopt;
- }
- return {anomalyTracker};
-}
-
-bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
- unordered_map<int64_t, int>& atomMatchingTrackerMap,
- vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- set<int>& allTagIds) {
- vector<AtomMatcher> matcherConfigs;
- const int atomMatcherCount = config.atom_matcher_size();
- matcherConfigs.reserve(atomMatcherCount);
- allAtomMatchingTrackers.reserve(atomMatcherCount);
-
- for (int i = 0; i < atomMatcherCount; i++) {
- const AtomMatcher& logMatcher = config.atom_matcher(i);
- sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap);
- if (tracker == nullptr) {
- return false;
- }
- allAtomMatchingTrackers.push_back(tracker);
- if (atomMatchingTrackerMap.find(logMatcher.id()) != atomMatchingTrackerMap.end()) {
- ALOGE("Duplicate AtomMatcher found!");
- return false;
- }
- atomMatchingTrackerMap[logMatcher.id()] = i;
- matcherConfigs.push_back(logMatcher);
- }
-
- vector<bool> stackTracker2(allAtomMatchingTrackers.size(), false);
- for (auto& matcher : allAtomMatchingTrackers) {
- if (!matcher->init(matcherConfigs, allAtomMatchingTrackers, atomMatchingTrackerMap,
- stackTracker2)) {
- return false;
- }
- // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
- const set<int>& tagIds = matcher->getAtomIds();
- allTagIds.insert(tagIds.begin(), tagIds.end());
- }
- return true;
-}
-
-bool initConditions(const ConfigKey& key, const StatsdConfig& config,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- unordered_map<int64_t, int>& conditionTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- unordered_map<int, std::vector<int>>& trackerToConditionMap,
- vector<ConditionState>& initialConditionCache) {
- vector<Predicate> conditionConfigs;
- const int conditionTrackerCount = config.predicate_size();
- conditionConfigs.reserve(conditionTrackerCount);
- allConditionTrackers.reserve(conditionTrackerCount);
- initialConditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
-
- for (int i = 0; i < conditionTrackerCount; i++) {
- const Predicate& condition = config.predicate(i);
- sp<ConditionTracker> tracker =
- createConditionTracker(key, condition, i, atomMatchingTrackerMap);
- if (tracker == nullptr) {
- return false;
- }
- allConditionTrackers.push_back(tracker);
- if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
- ALOGE("Duplicate Predicate found!");
- return false;
- }
- conditionTrackerMap[condition.id()] = i;
- conditionConfigs.push_back(condition);
- }
-
- vector<bool> stackTracker(allConditionTrackers.size(), false);
- for (size_t i = 0; i < allConditionTrackers.size(); i++) {
- auto& conditionTracker = allConditionTrackers[i];
- if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
- stackTracker, initialConditionCache)) {
- return false;
- }
- for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
- auto& conditionList = trackerToConditionMap[trackerIndex];
- conditionList.push_back(i);
- }
- }
- return true;
-}
-
-bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
- unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- map<int64_t, uint64_t>& stateProtoHashes) {
- 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();
-
- string serializedState;
- if (!state.SerializeToString(&serializedState)) {
- ALOGE("Unable to serialize state %lld", (long long)stateId);
- return false;
- }
- stateProtoHashes[stateId] = Hash64(serializedState);
-
- 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, const sp<StatsPullerManager>& pullerManager,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const vector<ConditionState>& initialConditionCache,
- vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int, vector<int>>& conditionToMetricMap,
- unordered_map<int, vector<int>>& trackerToMetricMap,
- unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
- unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
- sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
- const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
- config.event_metric_size() + config.gauge_metric_size() +
- config.value_metric_size();
- allMetricProducers.reserve(allMetricsCount);
-
- // Construct map from metric id to metric activation index. The map will be used to determine
- // the metric activation corresponding to a metric.
- unordered_map<int64_t, int> metricToActivationMap;
- for (int i = 0; i < config.metric_activation_size(); i++) {
- const MetricActivation& metricActivation = config.metric_activation(i);
- int64_t metricId = metricActivation.metric_id();
- if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
- ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
- return false;
- }
- metricToActivationMap.insert({metricId, i});
- }
-
- // Build MetricProducers for each metric defined in config.
- // build CountMetricProducer
- for (int i = 0; i < config.count_metric_size(); i++) {
- int metricIndex = allMetricProducers.size();
- const CountMetric& metric = config.count_metric(i);
- metricMap.insert({metric.id(), metricIndex});
- optional<sp<MetricProducer>> producer = createCountMetricProducerAndUpdateMetadata(
- key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
- allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
- allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation);
- if (!producer) {
- return false;
- }
- allMetricProducers.push_back(producer.value());
- }
-
- // build DurationMetricProducer
- for (int i = 0; i < config.duration_metric_size(); i++) {
- int metricIndex = allMetricProducers.size();
- const DurationMetric& metric = config.duration_metric(i);
- metricMap.insert({metric.id(), metricIndex});
-
- optional<sp<MetricProducer>> producer = createDurationMetricProducerAndUpdateMetadata(
- key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
- allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
- allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation);
- if (!producer) {
- return false;
- }
- allMetricProducers.push_back(producer.value());
- }
-
- // build EventMetricProducer
- for (int i = 0; i < config.event_metric_size(); i++) {
- int metricIndex = allMetricProducers.size();
- const EventMetric& metric = config.event_metric(i);
- metricMap.insert({metric.id(), metricIndex});
- optional<sp<MetricProducer>> producer = createEventMetricProducerAndUpdateMetadata(
- key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers,
- atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
- initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
- conditionToMetricMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation);
- if (!producer) {
- return false;
- }
- allMetricProducers.push_back(producer.value());
- }
-
- // build ValueMetricProducer
- for (int i = 0; i < config.value_metric_size(); i++) {
- int metricIndex = allMetricProducers.size();
- const ValueMetric& metric = config.value_metric(i);
- metricMap.insert({metric.id(), metricIndex});
- optional<sp<MetricProducer>> producer = createValueMetricProducerAndUpdateMetadata(
- key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
- allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap,
- allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation);
- if (!producer) {
- return false;
- }
- allMetricProducers.push_back(producer.value());
- }
-
- // Gauge metrics.
- for (int i = 0; i < config.gauge_metric_size(); i++) {
- int metricIndex = allMetricProducers.size();
- const GaugeMetric& metric = config.gauge_metric(i);
- metricMap.insert({metric.id(), metricIndex});
- optional<sp<MetricProducer>> producer = createGaugeMetricProducerAndUpdateMetadata(
- key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
- allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
- conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
- metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation);
- if (!producer) {
- return false;
- }
- allMetricProducers.push_back(producer.value());
- }
- for (int i = 0; i < config.no_report_metric_size(); ++i) {
- const auto no_report_metric = config.no_report_metric(i);
- if (metricMap.find(no_report_metric) == metricMap.end()) {
- ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric);
- return false;
- }
- noReportMetricIds.insert(no_report_metric);
- }
-
- const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
- config.whitelisted_atom_ids().end());
- for (const auto& it : allMetricProducers) {
- // Register metrics to StateTrackers
- for (int atomId : it->getSlicedStateAtoms()) {
- // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
- // state atom is not allowed.
- if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
- StateManager::getInstance().registerListener(atomId, it);
- } else {
- return false;
- }
- }
- }
- return true;
-}
-
-bool initAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap,
- unordered_map<int64_t, int>& alertTrackerMap,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- vector<sp<MetricProducer>>& allMetricProducers,
- vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
- for (int i = 0; i < config.alert_size(); i++) {
- const Alert& alert = config.alert(i);
- alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
- optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker(
- alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers);
- if (!anomalyTracker) {
- return false;
- }
- allAnomalyTrackers.push_back(anomalyTracker.value());
- }
- if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, alertTrackerMap,
- allAnomalyTrackers)) {
- return false;
- }
- return true;
-}
-
-bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs, vector<sp<AlarmTracker>>& allAlarmTrackers) {
- unordered_map<int64_t, int> alarmTrackerMap;
- int64_t startMillis = timeBaseNs / 1000 / 1000;
- int64_t currentTimeMillis = currentTimeNs / 1000 / 1000;
- for (int i = 0; i < config.alarm_size(); i++) {
- const Alarm& alarm = config.alarm(i);
- if (alarm.offset_millis() <= 0) {
- ALOGW("Alarm offset_millis should be larger than 0.");
- return false;
- }
- if (alarm.period_millis() <= 0) {
- ALOGW("Alarm period_millis should be larger than 0.");
- return false;
- }
- alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
- allAlarmTrackers.push_back(
- new AlarmTracker(startMillis, currentTimeMillis, alarm, key, periodicAlarmMonitor));
- }
- if (!initSubscribersForSubscriptionType(config, Subscription::ALARM, alarmTrackerMap,
- allAlarmTrackers)) {
- return false;
- }
- return true;
-}
-
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs, set<int>& allTagIds,
- vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- unordered_map<int64_t, int>& atomMatchingTrackerMap,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- unordered_map<int64_t, int>& conditionTrackerMap,
- vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int64_t, int>& metricProducerMap,
- vector<sp<AnomalyTracker>>& allAnomalyTrackers,
- vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
- unordered_map<int, std::vector<int>>& conditionToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToConditionMap,
- unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- unordered_map<int64_t, int>& alertTrackerMap,
- vector<int>& metricsWithActivation, map<int64_t, uint64_t>& stateProtoHashes,
- set<int64_t>& noReportMetricIds) {
- vector<ConditionState> initialConditionCache;
- unordered_map<int64_t, int> stateAtomIdMap;
- unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
-
- if (!initAtomMatchingTrackers(config, uidMap, atomMatchingTrackerMap, allAtomMatchingTrackers,
- allTagIds)) {
- ALOGE("initAtomMatchingTrackers failed");
- return false;
- }
- VLOG("initAtomMatchingTrackers succeed...");
-
- if (!initConditions(key, config, atomMatchingTrackerMap, conditionTrackerMap,
- allConditionTrackers, trackerToConditionMap, initialConditionCache)) {
- ALOGE("initConditionTrackers failed");
- return false;
- }
-
- if (!initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)) {
- ALOGE("initStates failed");
- return false;
- }
- if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, atomMatchingTrackerMap,
- conditionTrackerMap, allAtomMatchingTrackers, stateAtomIdMap,
- allStateGroupMaps, allConditionTrackers, initialConditionCache,
- allMetricProducers, conditionToMetricMap, trackerToMetricMap,
- metricProducerMap, noReportMetricIds, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- ALOGE("initMetricProducers failed");
- return false;
- }
- if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
- allMetricProducers, allAnomalyTrackers)) {
- ALOGE("initAlerts failed");
- return false;
- }
- if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
- allPeriodicAlarmTrackers)) {
- ALOGE("initAlarms failed");
- return false;
- }
-
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
deleted file mode 100644
index 84e1e4e04339..000000000000
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ /dev/null
@@ -1,342 +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.
- */
-
-#pragma once
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "anomaly/AlarmTracker.h"
-#include "condition/ConditionTracker.h"
-#include "external/StatsPullerManager.h"
-#include "matchers/AtomMatchingTracker.h"
-#include "metrics/MetricProducer.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Helper functions for creating, validating, and updating config components from StatsdConfig.
-// Should only be called from metrics_manager_util and config_update_utils.
-
-// Create a AtomMatchingTracker.
-// input:
-// [logMatcher]: the input AtomMatcher from the StatsdConfig
-// [index]: the index of the matcher
-// output:
-// new AtomMatchingTracker, or null if the tracker is unable to be created
-sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
- const sp<UidMap>& uidMap);
-
-// Create a ConditionTracker.
-// input:
-// [predicate]: the input Predicate from the StatsdConfig
-// [index]: the index of the condition tracker
-// [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers
-// output:
-// new ConditionTracker, or null if the tracker is unable to be created
-sp<ConditionTracker> createConditionTracker(
- const ConfigKey& key, const Predicate& predicate, const int index,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap);
-
-// Get the hash of a metric, combining the activation if the metric has one.
-bool getMetricProtoHash(const StatsdConfig& config, const google::protobuf::MessageLite& metric,
- const int64_t id,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- uint64_t& metricHash);
-
-// 1. Validates matcher existence
-// 2. Enforces matchers with dimensions and those used for trigger_event are about one atom
-// 3. Gets matcher index and updates tracker to metric map
-bool handleMetricWithAtomMatchingTrackers(
- const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex);
-
-// 1. Validates condition existence, including those in links
-// 2. Gets condition index and updates condition to metric map
-bool handleMetricWithConditions(
- const int64_t condition, const int metricIndex,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
- links,
- const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap);
-
-// Validates a metricActivation and populates state.
-// Fills the new event activation/deactivation maps, preserving the existing activations.
-// Returns false if there are errors.
-bool handleMetricActivationOnConfigUpdate(
- const StatsdConfig& config, const int64_t metricId, const int metricIndex,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
- const std::unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation,
- std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
- std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap);
-
-// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
-optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& stateAtomIdMap,
- const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
-// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
-optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& stateAtomIdMap,
- const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
-// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
-optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const EventMetric& metric, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
-// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
-optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const ValueMetric& metric, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::unordered_map<int64_t, int>& stateAtomIdMap,
- const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
-// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
-optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const GaugeMetric& metric, const int metricIndex,
- const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
- const sp<EventMatcherWizard>& matcherWizard,
- const std::unordered_map<int64_t, int>& metricToActivationMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
-// Creates an AnomalyTracker and adds it to the appropriate metric.
-// Returns an sp to the AnomalyTracker, or nullopt if there was an error.
-optional<sp<AnomalyTracker>> createAnomalyTracker(
- const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const std::unordered_map<int64_t, int>& metricProducerMap,
- std::vector<sp<MetricProducer>>& allMetricProducers);
-
-// Templated function for adding subscriptions to alarms or alerts. Returns true if successful.
-template <typename T>
-bool initSubscribersForSubscriptionType(const StatsdConfig& config,
- const Subscription_RuleType ruleType,
- const std::unordered_map<int64_t, int>& ruleMap,
- std::vector<T>& allRules) {
- for (int i = 0; i < config.subscription_size(); ++i) {
- const Subscription& subscription = config.subscription(i);
- if (subscription.rule_type() != ruleType) {
- continue;
- }
- if (subscription.subscriber_information_case() ==
- Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
- ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id());
- return false;
- }
- const auto& itr = ruleMap.find(subscription.rule_id());
- if (itr == ruleMap.end()) {
- ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
- (long long)subscription.id(), (long long)subscription.rule_id());
- return false;
- }
- const int ruleIndex = itr->second;
- allRules[ruleIndex]->addSubscription(subscription);
- }
- return true;
-}
-
-// Helper functions for MetricsManager to initialize from StatsdConfig.
-// *Note*: only initStatsdConfig() should be called from outside.
-// All other functions are intermediate
-// steps, created to make unit tests easier. And most of the parameters in these
-// functions are temporary objects in the initialization phase.
-
-// Initialize the AtomMatchingTrackers.
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input StatsdConfig
-// output:
-// [atomMatchingTrackerMap]: this map should contain matcher name to index mapping
-// [allAtomMatchingTrackers]: should store the sp to all the AtomMatchingTracker
-// [allTagIds]: contains the set of all interesting tag ids to this config.
-bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
- std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::set<int>& allTagIds);
-
-// Initialize ConditionTrackers
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input config
-// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
-// output:
-// [conditionTrackerMap]: this map should contain condition name to index mapping
-// [allConditionTrackers]: stores the sp to all the ConditionTrackers
-// [trackerToConditionMap]: contain the mapping from index of
-// log tracker to condition trackers that use the log tracker
-// [initialConditionCache]: stores the initial conditions for each ConditionTracker
-bool initConditions(const ConfigKey& key, const StatsdConfig& config,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::unordered_map<int64_t, int>& conditionTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- std::vector<ConditionState>& initialConditionCache);
-
-// 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
-// [stateProtoHashes]: contains a map of state id to the hash of the State proto from the config
-bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
- unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- std::map<int64_t, uint64_t>& stateProtoHashes);
-
-// Initialize MetricProducers.
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input config
-// [timeBaseSec]: start time base for all metrics
-// [atomMatchingTrackerMap]: AtomMatchingTracker 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
-// the list of MetricProducer index
-// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
-bool initMetrics(
- const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
- const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
- const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- const std::unordered_map<int64_t, int>& conditionTrackerMap,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& stateAtomIdMap,
- const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
- vector<sp<ConditionTracker>>& allConditionTrackers,
- const std::vector<ConditionState>& initialConditionCache,
- std::vector<sp<MetricProducer>>& allMetricProducers,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::set<int64_t>& noReportMetricIds,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::vector<int>& metricsWithActivation);
-
-// Initialize alarms
-// Is called both on initialize new configs and config updates since alarms do not have any state.
-bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs, std::vector<sp<AlarmTracker>>& allAlarmTrackers);
-
-// Initialize MetricsManager from StatsdConfig.
-// Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
- const sp<StatsPullerManager>& pullerManager,
- const sp<AlarmMonitor>& anomalyAlarmMonitor,
- const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
- const int64_t currentTimeNs, std::set<int>& allTagIds,
- std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
- std::vector<sp<ConditionTracker>>& allConditionTrackers,
- std::unordered_map<int64_t, int>& conditionTrackerMap,
- std::vector<sp<MetricProducer>>& allMetricProducers,
- std::unordered_map<int64_t, int>& metricProducerMap,
- vector<sp<AnomalyTracker>>& allAnomalyTrackers,
- vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
- std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- std::unordered_map<int64_t, int>& alertTrackerMap,
- std::vector<int>& metricsWithActivation,
- std::map<int64_t, uint64_t>& stateProtoHashes,
- std::set<int64_t>& noReportMetricIds);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h
deleted file mode 100644
index 1bc84c5433f9..000000000000
--- a/cmds/statsd/src/packages/PackageInfoListener.h
+++ /dev/null
@@ -1,47 +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.
- */
-
-#ifndef STATSD_PACKAGE_INFO_LISTENER_H
-#define STATSD_PACKAGE_INFO_LISTENER_H
-
-#include <utils/RefBase.h>
-
-#include <string>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class PackageInfoListener : public virtual android::RefBase {
-public:
- // Uid map will notify this listener that the app with apk name and uid has been upgraded to
- // the specified version.
- virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const std::string& apk,
- const int uid, const int64_t version) = 0;
-
- // Notify interested listeners that the given apk and uid combination no longer exits.
- virtual void notifyAppRemoved(const int64_t& eventTimeNs, const std::string& apk,
- const int uid) = 0;
-
- // Notify the listener that the UidMap snapshot is available.
- virtual void onUidMapReceived(const int64_t& eventTimeNs) = 0;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // STATSD_PACKAGE_INFO_LISTENER_H
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
deleted file mode 100644
index acf40c88a00d..000000000000
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, versionCode 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "hash.h"
-#include "stats_log_util.h"
-#include "guardrail/StatsdStats.h"
-#include "packages/UidMap.h"
-
-#include <inttypes.h>
-
-using namespace android;
-
-using android::base::StringPrintf;
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_FLOAT;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_UINT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::ProtoOutputStream;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const int FIELD_ID_SNAPSHOT_PACKAGE_NAME = 1;
-const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION = 2;
-const int FIELD_ID_SNAPSHOT_PACKAGE_UID = 3;
-const int FIELD_ID_SNAPSHOT_PACKAGE_DELETED = 4;
-const int FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH = 5;
-const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING = 6;
-const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH = 7;
-const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER = 8;
-const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH = 9;
-const int FIELD_ID_SNAPSHOT_TIMESTAMP = 1;
-const int FIELD_ID_SNAPSHOT_PACKAGE_INFO = 2;
-const int FIELD_ID_SNAPSHOTS = 1;
-const int FIELD_ID_CHANGES = 2;
-const int FIELD_ID_CHANGE_DELETION = 1;
-const int FIELD_ID_CHANGE_TIMESTAMP = 2;
-const int FIELD_ID_CHANGE_PACKAGE = 3;
-const int FIELD_ID_CHANGE_UID = 4;
-const int FIELD_ID_CHANGE_NEW_VERSION = 5;
-const int FIELD_ID_CHANGE_PREV_VERSION = 6;
-const int FIELD_ID_CHANGE_PACKAGE_HASH = 7;
-const int FIELD_ID_CHANGE_NEW_VERSION_STRING = 8;
-const int FIELD_ID_CHANGE_PREV_VERSION_STRING = 9;
-const int FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH = 10;
-const int FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH = 11;
-
-UidMap::UidMap() : mBytesUsed(0) {}
-
-UidMap::~UidMap() {}
-
-sp<UidMap> UidMap::getInstance() {
- static sp<UidMap> sInstance = new UidMap();
- return sInstance;
-}
-
-bool UidMap::hasApp(int uid, const string& packageName) const {
- lock_guard<mutex> lock(mMutex);
-
- auto it = mMap.find(std::make_pair(uid, packageName));
- return it != mMap.end() && !it->second.deleted;
-}
-
-string UidMap::normalizeAppName(const string& appName) const {
- string normalizedName = appName;
- std::transform(normalizedName.begin(), normalizedName.end(), normalizedName.begin(), ::tolower);
- return normalizedName;
-}
-
-std::set<string> UidMap::getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const {
- lock_guard<mutex> lock(mMutex);
- return getAppNamesFromUidLocked(uid,returnNormalized);
-}
-
-std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const {
- std::set<string> names;
- for (const auto& kv : mMap) {
- if (kv.first.first == uid && !kv.second.deleted) {
- names.insert(returnNormalized ? normalizeAppName(kv.first.second) : kv.first.second);
- }
- }
- return names;
-}
-
-int64_t UidMap::getAppVersion(int uid, const string& packageName) const {
- lock_guard<mutex> lock(mMutex);
-
- auto it = mMap.find(std::make_pair(uid, packageName));
- if (it == mMap.end() || it->second.deleted) {
- return 0;
- }
- return it->second.versionCode;
-}
-
-void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
- const vector<int64_t>& versionCode, const vector<String16>& versionString,
- const vector<String16>& packageName, const vector<String16>& installer) {
- wp<PackageInfoListener> broadcast = NULL;
- {
- lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
-
- std::unordered_map<std::pair<int, string>, AppData, PairHash> deletedApps;
-
- // Copy all the deleted apps.
- for (const auto& kv : mMap) {
- if (kv.second.deleted) {
- deletedApps[kv.first] = kv.second;
- }
- }
-
- mMap.clear();
- for (size_t j = 0; j < uid.size(); j++) {
- string package = string(String8(packageName[j]).string());
- mMap[std::make_pair(uid[j], package)] =
- AppData(versionCode[j], string(String8(versionString[j]).string()),
- string(String8(installer[j]).string()));
- }
-
- for (const auto& kv : deletedApps) {
- auto mMapIt = mMap.find(kv.first);
- if (mMapIt != mMap.end()) {
- // Insert this deleted app back into the current map.
- mMap[kv.first] = kv.second;
- }
- }
-
- ensureBytesUsedBelowLimit();
- StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
- broadcast = mSubscriber;
- }
- // To avoid invoking callback while holding the internal lock. we get a copy of the listener
- // and invoke the callback. It's still possible that after we copy the listener, it removes
- // itself before we call it. It's then the listener's job to handle it (expect the callback to
- // be called after listener is removed, and the listener should properly ignore it).
- auto strongPtr = broadcast.promote();
- if (strongPtr != NULL) {
- strongPtr->onUidMapReceived(timestamp);
- }
-}
-
-void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
- const int64_t& versionCode, const String16& versionString,
- const String16& installer) {
- wp<PackageInfoListener> broadcast = NULL;
- string appName = string(String8(app_16).string());
- {
- lock_guard<mutex> lock(mMutex);
- int32_t prevVersion = 0;
- string prevVersionString = "";
- string newVersionString = string(String8(versionString).string());
- bool found = false;
- auto it = mMap.find(std::make_pair(uid, appName));
- if (it != mMap.end()) {
- found = true;
- prevVersion = it->second.versionCode;
- prevVersionString = it->second.versionString;
- it->second.versionCode = versionCode;
- it->second.versionString = newVersionString;
- it->second.installer = string(String8(installer).string());
- it->second.deleted = false;
- }
- if (!found) {
- // Otherwise, we need to add an app at this uid.
- mMap[std::make_pair(uid, appName)] =
- AppData(versionCode, newVersionString, string(String8(installer).string()));
- } else {
- // Only notify the listeners if this is an app upgrade. If this app is being installed
- // for the first time, then we don't notify the listeners.
- // It's also OK to split again if we're forming a partial bucket after re-installing an
- // app after deletion.
- broadcast = mSubscriber;
- }
- mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString,
- prevVersion, prevVersionString);
- mBytesUsed += kBytesChangeRecord;
- ensureBytesUsedBelowLimit();
- StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
- StatsdStats::getInstance().setUidMapChanges(mChanges.size());
- }
-
- auto strongPtr = broadcast.promote();
- if (strongPtr != NULL) {
- strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
- }
-}
-
-void UidMap::ensureBytesUsedBelowLimit() {
- size_t limit;
- if (maxBytesOverride <= 0) {
- limit = StatsdStats::kMaxBytesUsedUidMap;
- } else {
- limit = maxBytesOverride;
- }
- while (mBytesUsed > limit) {
- ALOGI("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit);
- if (mChanges.size() > 0) {
- mBytesUsed -= kBytesChangeRecord;
- mChanges.pop_front();
- StatsdStats::getInstance().noteUidMapDropped(1);
- }
- }
-}
-
-void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
- wp<PackageInfoListener> broadcast = NULL;
- string app = string(String8(app_16).string());
- {
- lock_guard<mutex> lock(mMutex);
-
- int64_t prevVersion = 0;
- string prevVersionString = "";
- auto key = std::make_pair(uid, app);
- auto it = mMap.find(key);
- if (it != mMap.end() && !it->second.deleted) {
- prevVersion = it->second.versionCode;
- prevVersionString = it->second.versionString;
- it->second.deleted = true;
- mDeletedApps.push_back(key);
- }
- if (mDeletedApps.size() > StatsdStats::kMaxDeletedAppsInUidMap) {
- // Delete the oldest one.
- auto oldest = mDeletedApps.front();
- mDeletedApps.pop_front();
- mMap.erase(oldest);
- StatsdStats::getInstance().noteUidMapAppDeletionDropped();
- }
- mChanges.emplace_back(true, timestamp, app, uid, 0, "", prevVersion, prevVersionString);
- mBytesUsed += kBytesChangeRecord;
- ensureBytesUsedBelowLimit();
- StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
- StatsdStats::getInstance().setUidMapChanges(mChanges.size());
- broadcast = mSubscriber;
- }
-
- auto strongPtr = broadcast.promote();
- if (strongPtr != NULL) {
- strongPtr->notifyAppRemoved(timestamp, app, uid);
- }
-}
-
-void UidMap::setListener(wp<PackageInfoListener> listener) {
- lock_guard<mutex> lock(mMutex); // Lock for updates
- mSubscriber = listener;
-}
-
-void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) {
- lock_guard<mutex> lock(mIsolatedMutex);
-
- mIsolatedUidMap[isolatedUid] = parentUid;
-}
-
-void UidMap::removeIsolatedUid(int isolatedUid) {
- lock_guard<mutex> lock(mIsolatedMutex);
-
- auto it = mIsolatedUidMap.find(isolatedUid);
- if (it != mIsolatedUidMap.end()) {
- mIsolatedUidMap.erase(it);
- }
-}
-
-int UidMap::getHostUidOrSelf(int uid) const {
- lock_guard<mutex> lock(mIsolatedMutex);
-
- auto it = mIsolatedUidMap.find(uid);
- if (it != mIsolatedUidMap.end()) {
- return it->second;
- }
- return uid;
-}
-
-void UidMap::clearOutput() {
- mChanges.clear();
- // Also update the guardrail trackers.
- StatsdStats::getInstance().setUidMapChanges(0);
- mBytesUsed = 0;
- StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
-}
-
-int64_t UidMap::getMinimumTimestampNs() {
- int64_t m = 0;
- for (const auto& kv : mLastUpdatePerConfigKey) {
- if (m == 0) {
- m = kv.second;
- } else if (kv.second < m) {
- m = kv.second;
- }
- }
- return m;
-}
-
-size_t UidMap::getBytesUsed() const {
- return mBytesUsed;
-}
-
-void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings,
- bool includeInstaller, const std::set<int32_t>& interestingUids,
- std::set<string>* str_set, ProtoOutputStream* proto) {
- lock_guard<mutex> lock(mMutex);
-
- writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids,
- str_set, proto);
-}
-
-void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
- bool includeInstaller,
- const std::set<int32_t>& interestingUids,
- std::set<string>* str_set, ProtoOutputStream* proto) {
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
- for (const auto& kv : mMap) {
- if (!interestingUids.empty() &&
- interestingUids.find(kv.first.first) == interestingUids.end()) {
- continue;
- }
- uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- FIELD_ID_SNAPSHOT_PACKAGE_INFO);
- if (str_set != nullptr) {
- str_set->insert(kv.first.second);
- proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
- (long long)Hash64(kv.first.second));
- if (includeVersionStrings) {
- str_set->insert(kv.second.versionString);
- proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
- (long long)Hash64(kv.second.versionString));
- }
- if (includeInstaller) {
- str_set->insert(kv.second.installer);
- proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
- (long long)Hash64(kv.second.installer));
- }
- } else {
- proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
- if (includeVersionStrings) {
- proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
- kv.second.versionString);
- }
- if (includeInstaller) {
- proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
- kv.second.installer);
- }
- }
-
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
- (long long)kv.second.versionCode);
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
- proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
- proto->end(token);
- }
-}
-
-void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
- bool includeVersionStrings, bool includeInstaller,
- ProtoOutputStream* proto) {
- lock_guard<mutex> lock(mMutex); // Lock for updates
-
- for (const ChangeRecord& record : mChanges) {
- if (record.timestampNs > mLastUpdatePerConfigKey[key]) {
- uint64_t changesToken =
- proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CHANGES);
- proto->write(FIELD_TYPE_BOOL | FIELD_ID_CHANGE_DELETION, (bool)record.deletion);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_TIMESTAMP,
- (long long)record.timestampNs);
- if (str_set != nullptr) {
- str_set->insert(record.package);
- proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PACKAGE_HASH,
- (long long)Hash64(record.package));
- if (includeVersionStrings) {
- str_set->insert(record.versionString);
- proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH,
- (long long)Hash64(record.versionString));
- str_set->insert(record.prevVersionString);
- proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH,
- (long long)Hash64(record.prevVersionString));
- }
- } else {
- proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
- if (includeVersionStrings) {
- proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_NEW_VERSION_STRING,
- record.versionString);
- proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PREV_VERSION_STRING,
- record.prevVersionString);
- }
- }
-
- proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_NEW_VERSION, (long long)record.version);
- proto->write(FIELD_TYPE_INT64 | FIELD_ID_CHANGE_PREV_VERSION,
- (long long)record.prevVersion);
- proto->end(changesToken);
- }
- }
-
- // Write snapshot from current uid map state.
- uint64_t snapshotsToken =
- proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
- writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller,
- std::set<int32_t>() /*empty uid set means including every uid*/,
- str_set, proto);
- proto->end(snapshotsToken);
-
- int64_t prevMin = getMinimumTimestampNs();
- mLastUpdatePerConfigKey[key] = timestamp;
- int64_t newMin = getMinimumTimestampNs();
-
- if (newMin > prevMin) { // Delete anything possible now that the minimum has
- // moved forward.
- int64_t cutoff_nanos = newMin;
- for (auto it_changes = mChanges.begin(); it_changes != mChanges.end();) {
- if (it_changes->timestampNs < cutoff_nanos) {
- mBytesUsed -= kBytesChangeRecord;
- it_changes = mChanges.erase(it_changes);
- } else {
- ++it_changes;
- }
- }
- }
- StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
- StatsdStats::getInstance().setUidMapChanges(mChanges.size());
-}
-
-void UidMap::printUidMap(int out) const {
- lock_guard<mutex> lock(mMutex);
-
- for (const auto& kv : mMap) {
- if (!kv.second.deleted) {
- dprintf(out, "%s, v%" PRId64 ", %s, %s (%i)\n", kv.first.second.c_str(),
- kv.second.versionCode, kv.second.versionString.c_str(),
- kv.second.installer.c_str(), kv.first.first);
- }
- }
-}
-
-void UidMap::OnConfigUpdated(const ConfigKey& key) {
- mLastUpdatePerConfigKey[key] = -1;
-}
-
-void UidMap::OnConfigRemoved(const ConfigKey& key) {
- mLastUpdatePerConfigKey.erase(key);
-}
-
-set<int32_t> UidMap::getAppUid(const string& package) const {
- lock_guard<mutex> lock(mMutex);
-
- set<int32_t> results;
- for (const auto& kv : mMap) {
- if (kv.first.second == package && !kv.second.deleted) {
- results.insert(kv.first.first);
- }
- }
- return results;
-}
-
-// Note not all the following AIDs are used as uids. Some are used only for gids.
-// It's ok to leave them in the map, but we won't ever see them in the log's uid field.
-// App's uid starts from 10000, and will not overlap with the following AIDs.
-const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0},
- {"AID_SYSTEM", 1000},
- {"AID_RADIO", 1001},
- {"AID_BLUETOOTH", 1002},
- {"AID_GRAPHICS", 1003},
- {"AID_INPUT", 1004},
- {"AID_AUDIO", 1005},
- {"AID_CAMERA", 1006},
- {"AID_LOG", 1007},
- {"AID_COMPASS", 1008},
- {"AID_MOUNT", 1009},
- {"AID_WIFI", 1010},
- {"AID_ADB", 1011},
- {"AID_INSTALL", 1012},
- {"AID_MEDIA", 1013},
- {"AID_DHCP", 1014},
- {"AID_SDCARD_RW", 1015},
- {"AID_VPN", 1016},
- {"AID_KEYSTORE", 1017},
- {"AID_USB", 1018},
- {"AID_DRM", 1019},
- {"AID_MDNSR", 1020},
- {"AID_GPS", 1021},
- // {"AID_UNUSED1", 1022},
- {"AID_MEDIA_RW", 1023},
- {"AID_MTP", 1024},
- // {"AID_UNUSED2", 1025},
- {"AID_DRMRPC", 1026},
- {"AID_NFC", 1027},
- {"AID_SDCARD_R", 1028},
- {"AID_CLAT", 1029},
- {"AID_LOOP_RADIO", 1030},
- {"AID_MEDIA_DRM", 1031},
- {"AID_PACKAGE_INFO", 1032},
- {"AID_SDCARD_PICS", 1033},
- {"AID_SDCARD_AV", 1034},
- {"AID_SDCARD_ALL", 1035},
- {"AID_LOGD", 1036},
- {"AID_SHARED_RELRO", 1037},
- {"AID_DBUS", 1038},
- {"AID_TLSDATE", 1039},
- {"AID_MEDIA_EX", 1040},
- {"AID_AUDIOSERVER", 1041},
- {"AID_METRICS_COLL", 1042},
- {"AID_METRICSD", 1043},
- {"AID_WEBSERV", 1044},
- {"AID_DEBUGGERD", 1045},
- {"AID_MEDIA_CODEC", 1046},
- {"AID_CAMERASERVER", 1047},
- {"AID_FIREWALL", 1048},
- {"AID_TRUNKS", 1049},
- {"AID_NVRAM", 1050},
- {"AID_DNS", 1051},
- {"AID_DNS_TETHER", 1052},
- {"AID_WEBVIEW_ZYGOTE", 1053},
- {"AID_VEHICLE_NETWORK", 1054},
- {"AID_MEDIA_AUDIO", 1055},
- {"AID_MEDIA_VIDEO", 1056},
- {"AID_MEDIA_IMAGE", 1057},
- {"AID_TOMBSTONED", 1058},
- {"AID_MEDIA_OBB", 1059},
- {"AID_ESE", 1060},
- {"AID_OTA_UPDATE", 1061},
- {"AID_AUTOMOTIVE_EVS", 1062},
- {"AID_LOWPAN", 1063},
- {"AID_HSM", 1064},
- {"AID_RESERVED_DISK", 1065},
- {"AID_STATSD", 1066},
- {"AID_INCIDENTD", 1067},
- {"AID_SECURE_ELEMENT", 1068},
- {"AID_LMKD", 1069},
- {"AID_LLKD", 1070},
- {"AID_IORAPD", 1071},
- {"AID_GPU_SERVICE", 1072},
- {"AID_NETWORK_STACK", 1073},
- {"AID_GSID", 1074},
- {"AID_FSVERITY_CERT", 1075},
- {"AID_CREDSTORE", 1076},
- {"AID_EXTERNAL_STORAGE", 1077},
- {"AID_EXT_DATA_RW", 1078},
- {"AID_EXT_OBB_RW", 1079},
- {"AID_CONTEXT_HUB", 1080},
- {"AID_SHELL", 2000},
- {"AID_CACHE", 2001},
- {"AID_DIAG", 2002}};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
deleted file mode 100644
index 622321b804ec..000000000000
--- a/cmds/statsd/src/packages/UidMap.h
+++ /dev/null
@@ -1,226 +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.
- */
-
-#pragma once
-
-#include "config/ConfigKey.h"
-#include "packages/PackageInfoListener.h"
-#include "stats_util.h"
-
-#include <gtest/gtest_prod.h>
-#include <stdio.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-
-#include <list>
-#include <mutex>
-#include <set>
-#include <string>
-#include <unordered_map>
-
-using namespace android;
-using namespace std;
-
-using android::util::ProtoOutputStream;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-struct AppData {
- int64_t versionCode;
- string versionString;
- string installer;
- bool deleted;
-
- // Empty constructor needed for unordered map.
- AppData() {
- }
-
- AppData(const int64_t v, const string& versionString, const string& installer)
- : versionCode(v), versionString(versionString), installer(installer), deleted(false){};
-};
-
-// When calling appendUidMap, we retrieve all the ChangeRecords since the last
-// timestamp we called appendUidMap for this configuration key.
-struct ChangeRecord {
- const bool deletion;
- const int64_t timestampNs;
- const string package;
- const int32_t uid;
- const int64_t version;
- const int64_t prevVersion;
- const string versionString;
- const string prevVersionString;
-
- ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package,
- const int32_t uid, const int64_t version, const string versionString,
- const int64_t prevVersion, const string prevVersionString)
- : deletion(isDeletion),
- timestampNs(timestampNs),
- package(package),
- uid(uid),
- version(version),
- prevVersion(prevVersion),
- versionString(versionString),
- prevVersionString(prevVersionString) {
- }
-};
-
-const unsigned int kBytesChangeRecord = sizeof(struct ChangeRecord);
-
-// UidMap keeps track of what the corresponding app name (APK name) and version code for every uid
-// at any given moment. This map must be updated by StatsCompanionService.
-class UidMap : public virtual android::RefBase {
-public:
- UidMap();
- ~UidMap();
- static const std::map<std::string, uint32_t> sAidToUidMapping;
-
- static sp<UidMap> getInstance();
- /*
- * All three inputs must be the same size, and the jth element in each array refers to the same
- * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
- */
- void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
- const vector<int64_t>& versionCode, const vector<String16>& versionString,
- const vector<String16>& packageName, const vector<String16>& installer);
-
- void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
- const int64_t& versionCode, const String16& versionString,
- const String16& installer);
- void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
-
- // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
- bool hasApp(int uid, const string& packageName) const;
-
- // Returns the app names from uid.
- std::set<string> getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const;
-
- int64_t getAppVersion(int uid, const string& packageName) const;
-
- // Helper for debugging contents of this uid map. Can be triggered with:
- // adb shell cmd stats print-uid-map
- void printUidMap(int outFd) const;
-
- // Command for indicating to the map that StatsLogProcessor should be notified if an app is
- // updated. This allows metric producers and managers to distinguish when the same uid or app
- // represents a different version of an app.
- void setListener(wp<PackageInfoListener> listener);
-
- // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date.
- void OnConfigUpdated(const ConfigKey& key);
-
- // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date.
- void OnConfigRemoved(const ConfigKey& key);
-
- void assignIsolatedUid(int isolatedUid, int parentUid);
- void removeIsolatedUid(int isolatedUid);
-
- // Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in.
- virtual int getHostUidOrSelf(int uid) const;
-
- // Gets all snapshots and changes that have occurred since the last output.
- // If every config key has received a change or snapshot record, then this
- // record is deleted.
- void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
- bool includeVersionStrings, bool includeInstaller,
- ProtoOutputStream* proto);
-
- // Forces the output to be cleared. We still generate a snapshot based on the current state.
- // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
- // in case we lose a previous upload.
- void clearOutput();
-
- // Get currently cached value of memory used by UID map.
- size_t getBytesUsed() const;
-
- virtual std::set<int32_t> getAppUid(const string& package) const;
-
- // Write current PackageInfoSnapshot to ProtoOutputStream.
- // interestingUids: If not empty, only write the package info for these uids. If empty, write
- // package info for all uids.
- // str_set: if not null, add new string to the set and write str_hash to proto
- // if null, write string to proto.
- void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller,
- const std::set<int32_t>& interestingUids, std::set<string>* str_set,
- ProtoOutputStream* proto);
-
-private:
- std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
- string normalizeAppName(const string& appName) const;
-
- void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
- bool includeInstaller, const std::set<int32_t>& interestingUids,
- std::set<string>* str_set, ProtoOutputStream* proto);
-
- mutable mutex mMutex;
- mutable mutex mIsolatedMutex;
-
- struct PairHash {
- size_t operator()(std::pair<int, string> p) const noexcept {
- std::hash<std::string> hash_fn;
- return hash_fn(std::to_string(p.first) + p.second);
- }
- };
- // Maps uid and package name to application data.
- std::unordered_map<std::pair<int, string>, AppData, PairHash> mMap;
-
- // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute
- // to the parent uid.
- std::unordered_map<int, int> mIsolatedUidMap;
-
- // Record the changes that can be provided with the uploads.
- std::list<ChangeRecord> mChanges;
-
- // Store which uid and apps represent deleted ones.
- std::list<std::pair<int, string>> mDeletedApps;
-
- // Notify StatsLogProcessor if there's an upgrade/removal in any app.
- wp<PackageInfoListener> mSubscriber;
-
- // Mapping of config keys we're aware of to the epoch time they last received an update. This
- // lets us know it's safe to delete events older than the oldest update. The value is nanosec.
- // Value of -1 denotes this config key has never received an upload.
- std::unordered_map<ConfigKey, int64_t> mLastUpdatePerConfigKey;
-
- // Returns the minimum value from mConfigKeys.
- int64_t getMinimumTimestampNs();
-
- // If our current used bytes is above the limit, then we clear out the earliest snapshot. If
- // there are no more snapshots, then we clear out the earliest delta. We repeat the deletions
- // until the memory consumed by mOutput is below the specified limit.
- void ensureBytesUsedBelowLimit();
-
- // Override used for testing the max memory allowed by uid map. 0 means we use the value
- // specified in StatsdStats.h with the rest of the guardrails.
- size_t maxBytesOverride = 0;
-
- // Cache the size of mOutput;
- size_t mBytesUsed;
-
- // Allows unit-test to access private methods.
- FRIEND_TEST(UidMapTest, TestClearingOutput);
- FRIEND_TEST(UidMapTest, TestRemovedAppRetained);
- FRIEND_TEST(UidMapTest, TestRemovedAppOverGuardrail);
- FRIEND_TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot);
- FRIEND_TEST(UidMapTest, TestMemoryComputed);
- FRIEND_TEST(UidMapTest, TestMemoryGuardrail);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
deleted file mode 100644
index 9d8f0c24e253..000000000000
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "ShellSubscriber.h"
-
-#include <android-base/file.h>
-
-#include "matchers/matcher_util.h"
-#include "stats_log_util.h"
-
-using android::util::ProtoOutputStream;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const static int FIELD_ID_ATOM = 1;
-
-void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
- int myToken = claimToken();
- VLOG("ShellSubscriber: new subscription %d has come in", myToken);
- mSubscriptionShouldEnd.notify_one();
-
- shared_ptr<SubscriptionInfo> mySubscriptionInfo = make_shared<SubscriptionInfo>(in, out);
- if (!readConfig(mySubscriptionInfo)) return;
-
- {
- std::unique_lock<std::mutex> lock(mMutex);
- mSubscriptionInfo = mySubscriptionInfo;
- spawnHelperThread(myToken);
- waitForSubscriptionToEndLocked(mySubscriptionInfo, myToken, lock, timeoutSec);
-
- if (mSubscriptionInfo == mySubscriptionInfo) {
- mSubscriptionInfo = nullptr;
- }
-
- }
-}
-
-void ShellSubscriber::spawnHelperThread(int myToken) {
- std::thread t([this, myToken] { pullAndSendHeartbeats(myToken); });
- t.detach();
-}
-
-void ShellSubscriber::waitForSubscriptionToEndLocked(shared_ptr<SubscriptionInfo> myInfo,
- int myToken,
- std::unique_lock<std::mutex>& lock,
- int timeoutSec) {
- if (timeoutSec > 0) {
- mSubscriptionShouldEnd.wait_for(lock, timeoutSec * 1s, [this, myToken, &myInfo] {
- return mToken != myToken || !myInfo->mClientAlive;
- });
- } else {
- mSubscriptionShouldEnd.wait(lock, [this, myToken, &myInfo] {
- return mToken != myToken || !myInfo->mClientAlive;
- });
- }
-}
-
-// Atomically claim the next token. Token numbers denote subscriber ordering.
-int ShellSubscriber::claimToken() {
- std::unique_lock<std::mutex> lock(mMutex);
- int myToken = ++mToken;
- return myToken;
-}
-
-// Read and parse single config. There should only one config per input.
-bool ShellSubscriber::readConfig(shared_ptr<SubscriptionInfo> subscriptionInfo) {
- // Read the size of the config.
- size_t bufferSize;
- if (!android::base::ReadFully(subscriptionInfo->mInputFd, &bufferSize, sizeof(bufferSize))) {
- return false;
- }
-
- // Read the config.
- vector<uint8_t> buffer(bufferSize);
- if (!android::base::ReadFully(subscriptionInfo->mInputFd, buffer.data(), bufferSize)) {
- return false;
- }
-
- // Parse the config.
- ShellSubscription config;
- if (!config.ParseFromArray(buffer.data(), bufferSize)) {
- return false;
- }
-
- // Update SubscriptionInfo with state from config
- for (const auto& pushed : config.pushed()) {
- subscriptionInfo->mPushedMatchers.push_back(pushed);
- }
-
- for (const auto& pulled : config.pulled()) {
- vector<string> packages;
- vector<int32_t> uids;
- for (const string& pkg : pulled.packages()) {
- auto it = UidMap::sAidToUidMapping.find(pkg);
- if (it != UidMap::sAidToUidMapping.end()) {
- uids.push_back(it->second);
- } else {
- packages.push_back(pkg);
- }
- }
-
- subscriptionInfo->mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis(), packages,
- uids);
- VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id());
- }
-
- return true;
-}
-
-void ShellSubscriber::pullAndSendHeartbeats(int myToken) {
- VLOG("ShellSubscriber: helper thread %d starting", myToken);
- while (true) {
- int64_t sleepTimeMs = INT_MAX;
- {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mSubscriptionInfo || mToken != myToken) {
- VLOG("ShellSubscriber: helper thread %d done!", myToken);
- return;
- }
-
- int64_t nowMillis = getElapsedRealtimeMillis();
- int64_t nowNanos = getElapsedRealtimeNs();
- for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
- if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) {
- continue;
- }
-
- vector<int32_t> uids;
- getUidsForPullAtom(&uids, pullInfo);
-
- vector<std::shared_ptr<LogEvent>> data;
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, nowNanos, &data);
- VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
- writePulledAtomsLocked(data, pullInfo.mPullerMatcher);
-
- pullInfo.mPrevPullElapsedRealtimeMs = nowMillis;
- }
-
- // Send a heartbeat, consisting of a data size of 0, if perfd hasn't recently received
- // data from statsd. When it receives the data size of 0, perfd will not expect any
- // atoms and recheck whether the subscription should end.
- if (nowMillis - mLastWriteMs > kMsBetweenHeartbeats) {
- attemptWriteToPipeLocked(/*dataSize=*/0);
- }
-
- // Determine how long to sleep before doing more work.
- for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
- int64_t nextPullTime = pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval;
- int64_t timeBeforePull = nextPullTime - nowMillis; // guaranteed to be non-negative
- if (timeBeforePull < sleepTimeMs) sleepTimeMs = timeBeforePull;
- }
- int64_t timeBeforeHeartbeat = (mLastWriteMs + kMsBetweenHeartbeats) - nowMillis;
- if (timeBeforeHeartbeat < sleepTimeMs) sleepTimeMs = timeBeforeHeartbeat;
- }
-
- VLOG("ShellSubscriber: helper thread %d sleeping for %lld ms", myToken,
- (long long)sleepTimeMs);
- std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs));
- }
-}
-
-void ShellSubscriber::getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo) {
- uids->insert(uids->end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end());
- // This is slow. Consider storing the uids per app and listening to uidmap updates.
- for (const string& pkg : pullInfo.mPullPackages) {
- set<int32_t> uidsForPkg = mUidMap->getAppUid(pkg);
- uids->insert(uids->end(), uidsForPkg.begin(), uidsForPkg.end());
- }
- uids->push_back(DEFAULT_PULL_UID);
-}
-
-void ShellSubscriber::writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
- const SimpleAtomMatcher& matcher) {
- mProto.clear();
- int count = 0;
- for (const auto& event : data) {
- if (matchesSimple(mUidMap, matcher, *event)) {
- count++;
- uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
- util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
- event->ToProto(mProto);
- mProto.end(atomToken);
- }
- }
-
- if (count > 0) attemptWriteToPipeLocked(mProto.size());
-}
-
-void ShellSubscriber::onLogEvent(const LogEvent& event) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mSubscriptionInfo) return;
-
- mProto.clear();
- for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) {
- if (matchesSimple(mUidMap, matcher, event)) {
- uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
- util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
- event.ToProto(mProto);
- mProto.end(atomToken);
- attemptWriteToPipeLocked(mProto.size());
- }
- }
-}
-
-// Tries to write the atom encoded in mProto to the pipe. If the write fails
-// because the read end of the pipe has closed, signals to other threads that
-// the subscription should end.
-void ShellSubscriber::attemptWriteToPipeLocked(size_t dataSize) {
- // First, write the payload size.
- if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &dataSize, sizeof(dataSize))) {
- mSubscriptionInfo->mClientAlive = false;
- mSubscriptionShouldEnd.notify_one();
- return;
- }
-
- // Then, write the payload if this is not just a heartbeat.
- if (dataSize > 0 && !mProto.flush(mSubscriptionInfo->mOutputFd)) {
- mSubscriptionInfo->mClientAlive = false;
- mSubscriptionShouldEnd.notify_one();
- return;
- }
-
- mLastWriteMs = getElapsedRealtimeMillis();
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
deleted file mode 100644
index 4c05fa7f71c2..000000000000
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android/util/ProtoOutputStream.h>
-#include <private/android_filesystem_config.h>
-
-#include <condition_variable>
-#include <mutex>
-#include <thread>
-
-#include "external/StatsPullerManager.h"
-#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "logd/LogEvent.h"
-#include "packages/UidMap.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Handles atoms subscription via shell cmd.
- *
- * A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client
- * communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms.
- * The atoms are sent back to the client in real time, as opposed to keeping the data in memory.
- * Shell clients do not subscribe aggregated metrics, as they are responsible for doing the
- * aggregation after receiving the atom events.
- *
- * Shell clients pass ShellSubscription in the proto binary format. Clients can update the
- * subscription by sending a new subscription. The new subscription would replace the old one.
- * Input data stream format is:
- *
- * |size_t|subscription proto|size_t|subscription proto|....
- *
- * statsd sends the events back in Atom proto binary format. Each Atom message is preceded
- * with sizeof(size_t) bytes indicating the size of the proto message payload.
- *
- * The stream would be in the following format:
- * |size_t|shellData proto|size_t|shellData proto|....
- *
- * Only one shell subscriber is allowed at a time because each shell subscriber blocks one thread
- * until it exits.
- */
-class ShellSubscriber : public virtual RefBase {
-public:
- ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
- : mUidMap(uidMap), mPullerMgr(pullerMgr){};
-
- void startNewSubscription(int inFd, int outFd, int timeoutSec);
-
- void onLogEvent(const LogEvent& event);
-
-private:
- struct PullInfo {
- PullInfo(const SimpleAtomMatcher& matcher, int64_t interval,
- const std::vector<std::string>& packages, const std::vector<int32_t>& uids)
- : mPullerMatcher(matcher),
- mInterval(interval),
- mPrevPullElapsedRealtimeMs(0),
- mPullPackages(packages),
- mPullUids(uids) {
- }
- SimpleAtomMatcher mPullerMatcher;
- int64_t mInterval;
- int64_t mPrevPullElapsedRealtimeMs;
- std::vector<std::string> mPullPackages;
- std::vector<int32_t> mPullUids;
- };
-
- struct SubscriptionInfo {
- SubscriptionInfo(const int& inputFd, const int& outputFd)
- : mInputFd(inputFd), mOutputFd(outputFd), mClientAlive(true) {
- }
-
- int mInputFd;
- int mOutputFd;
- std::vector<SimpleAtomMatcher> mPushedMatchers;
- std::vector<PullInfo> mPulledInfo;
- bool mClientAlive;
- };
-
- int claimToken();
-
- bool readConfig(std::shared_ptr<SubscriptionInfo> subscriptionInfo);
-
- void spawnHelperThread(int myToken);
-
- void waitForSubscriptionToEndLocked(std::shared_ptr<SubscriptionInfo> myInfo,
- int myToken,
- std::unique_lock<std::mutex>& lock,
- int timeoutSec);
-
- // Helper thread that pulls atoms at a regular frequency and sends
- // heartbeats to perfd if statsd hasn't recently sent any data. Statsd must
- // send heartbeats for perfd to escape a blocking read call and recheck if
- // the user has terminated the subscription.
- void pullAndSendHeartbeats(int myToken);
-
- void writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
- const SimpleAtomMatcher& matcher);
-
- void getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo);
-
- void attemptWriteToPipeLocked(size_t dataSize);
-
- sp<UidMap> mUidMap;
-
- sp<StatsPullerManager> mPullerMgr;
-
- android::util::ProtoOutputStream mProto;
-
- mutable std::mutex mMutex;
-
- std::condition_variable mSubscriptionShouldEnd;
-
- std::shared_ptr<SubscriptionInfo> mSubscriptionInfo = nullptr;
-
- int mToken = 0;
-
- const int32_t DEFAULT_PULL_UID = AID_SYSTEM;
-
- // Tracks when we last send data to perfd. We need that time to determine
- // when next to send a heartbeat.
- int64_t mLastWriteMs = 0;
- const int64_t kMsBetweenHeartbeats = 1000;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
deleted file mode 100644
index 07d0310ef2dd..000000000000
--- a/cmds/statsd/src/shell/shell_config.proto
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package android.os.statsd;
-
-option java_package = "com.android.os";
-option java_outer_classname = "ShellConfig";
-
-import "frameworks/base/cmds/statsd/src/statsd_config.proto";
-
-message PulledAtomSubscription {
- optional SimpleAtomMatcher matcher = 1;
-
- /* gap between two pulls in milliseconds */
- optional int32 freq_millis = 2;
-
- /* Packages that the pull is requested from */
- repeated string packages = 3;
-}
-
-message ShellSubscription {
- repeated SimpleAtomMatcher pushed = 1;
- repeated PulledAtomSubscription pulled = 2;
-} \ No newline at end of file
diff --git a/cmds/statsd/src/shell/shell_data.proto b/cmds/statsd/src/shell/shell_data.proto
deleted file mode 100644
index ec41cbc5caff..000000000000
--- a/cmds/statsd/src/shell/shell_data.proto
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package android.os.statsd;
-
-option java_package = "com.android.os.statsd";
-option java_outer_classname = "ShellDataProto";
-
-import "frameworks/proto_logging/stats/atoms.proto";
-
-// The output of shell subscription, including both pulled and pushed subscriptions.
-message ShellData {
- repeated Atom atom = 1;
-}
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
deleted file mode 100755
index b877cc9c352f..000000000000
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <ctype.h>
-#include <limits.h>
-#include <stdio.h>
-#include <sys/cdefs.h>
-#include <sys/prctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <cutils/sockets.h>
-
-#include "StatsSocketListener.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_log_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-StatsSocketListener::StatsSocketListener(std::shared_ptr<LogEventQueue> queue)
- : SocketListener(getLogSocket(), false /*start listen*/), mQueue(queue) {
-}
-
-StatsSocketListener::~StatsSocketListener() {
-}
-
-bool StatsSocketListener::onDataAvailable(SocketClient* cli) {
- static bool name_set;
- if (!name_set) {
- prctl(PR_SET_NAME, "statsd.writer");
- name_set = true;
- }
-
- // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
- char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
- struct iovec iov = {buffer, sizeof(buffer) - 1};
-
- alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
- struct msghdr hdr = {
- NULL, 0, &iov, 1, control, sizeof(control), 0,
- };
-
- int socket = cli->getSocket();
-
- // To clear the entire buffer is secure/safe, but this contributes to 1.68%
- // overhead under logging load. We are safe because we check counts, but
- // still need to clear null terminator
- // memset(buffer, 0, sizeof(buffer));
- ssize_t n = recvmsg(socket, &hdr, 0);
- if (n <= (ssize_t)(sizeof(android_log_header_t))) {
- return false;
- }
-
- buffer[n] = 0;
-
- struct ucred* cred = NULL;
-
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
- while (cmsg != NULL) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
- cred = (struct ucred*)CMSG_DATA(cmsg);
- break;
- }
- cmsg = CMSG_NXTHDR(&hdr, cmsg);
- }
-
- struct ucred fake_cred;
- if (cred == NULL) {
- cred = &fake_cred;
- cred->pid = 0;
- cred->uid = DEFAULT_OVERFLOWUID;
- }
-
- uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t);
- n -= sizeof(android_log_header_t);
-
- // When a log failed to write to statsd socket (e.g., due ot EBUSY), a special message would
- // be sent to statsd when the socket communication becomes available again.
- // The format is android_log_event_int_t with a single integer in the payload indicating the
- // number of logs that failed. (*FORMAT MUST BE IN SYNC WITH system/core/libstats*)
- // Note that all normal stats logs are in the format of event_list, so there won't be confusion.
- //
- // TODO(b/80538532): In addition to log it in StatsdStats, we should properly reset the config.
- if (n == sizeof(android_log_event_long_t)) {
- android_log_event_long_t* long_event = reinterpret_cast<android_log_event_long_t*>(ptr);
- if (long_event->payload.type == EVENT_TYPE_LONG) {
- int64_t composed_long = long_event->payload.data;
-
- // format:
- // |last_tag|dropped_count|
- int32_t dropped_count = (int32_t)(0xffffffff & composed_long);
- int32_t last_atom_tag = (int32_t)((0xffffffff00000000 & (uint64_t)composed_long) >> 32);
-
- ALOGE("Found dropped events: %d error %d last atom tag %d from uid %d", dropped_count,
- long_event->header.tag, last_atom_tag, cred->uid);
- StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(), dropped_count,
- long_event->header.tag, last_atom_tag, cred->uid,
- cred->pid);
- return true;
- }
- }
-
- // move past the 4-byte StatsEventTag
- uint8_t* msg = ptr + sizeof(uint32_t);
- uint32_t len = n - sizeof(uint32_t);
- uint32_t uid = cred->uid;
- uint32_t pid = cred->pid;
-
- int64_t oldestTimestamp;
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
- logEvent->parseBuffer(msg, len);
-
- if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) {
- StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
- }
-
- return true;
-}
-
-int StatsSocketListener::getLogSocket() {
- static const char socketName[] = "statsdw";
- int sock = android_get_control_socket(socketName);
-
- if (sock < 0) { // statsd started up in init.sh
- sock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
-
- int on = 1;
- if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
- return -1;
- }
- }
- return sock;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h
deleted file mode 100644
index 2167a56445b9..000000000000
--- a/cmds/statsd/src/socket/StatsSocketListener.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <sysutils/SocketListener.h>
-#include <utils/RefBase.h>
-#include "logd/LogEventQueue.h"
-
-// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
-// the uapi headers for userspace to use. This value is filled in on the
-// out-of-band socket credentials if the OS fails to find one available.
-// One of the causes of this is if SO_PASSCRED is set, all the packets before
-// that point will have this value. We also use it in a fake credential if
-// no socket credentials are supplied.
-#ifndef DEFAULT_OVERFLOWUID
-#define DEFAULT_OVERFLOWUID 65534
-#endif
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StatsSocketListener : public SocketListener, public virtual android::RefBase {
-public:
- explicit StatsSocketListener(std::shared_ptr<LogEventQueue> queue);
-
- virtual ~StatsSocketListener();
-
-protected:
- virtual bool onDataAvailable(SocketClient* cli);
-
-private:
- static int getLogSocket();
- /**
- * Who is going to get the events when they're read.
- */
- std::shared_ptr<LogEventQueue> mQueue;
-};
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h
deleted file mode 100644
index 63880017ca18..000000000000
--- a/cmds/statsd/src/state/StateListener.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <utils/RefBase.h>
-
-#include "HashableDimensionKey.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StateListener : public virtual RefBase {
-public:
- StateListener(){};
-
- virtual ~StateListener(){};
-
- /**
- * Interface for handling a state change.
- *
- * The old and new state values map to the original state values.
- * StateTrackers only track the original state values and are unaware
- * of higher-level state groups. MetricProducers hold information on
- * state groups and are responsible for mapping original state values to
- * the correct state group.
- *
- * [eventTimeNs]: Time of the state change log event.
- * [atomId]: The id of the state atom
- * [primaryKey]: The primary field values of the state atom
- * [oldState]: Previous state value before state change
- * [newState]: Current state value after state change
- */
- virtual void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const FieldValue& oldState,
- const FieldValue& newState) = 0;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
deleted file mode 100644
index c29afeb794fa..000000000000
--- a/cmds/statsd/src/state/StateManager.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StateManager.h"
-
-#include <private/android_filesystem_config.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-StateManager::StateManager()
- : mAllowedPkg({
- "com.android.systemui",
- }) {
-}
-
-StateManager& StateManager::getInstance() {
- static StateManager sStateManager;
- return sStateManager;
-}
-
-void StateManager::clear() {
- mStateTrackers.clear();
-}
-
-void StateManager::onLogEvent(const LogEvent& event) {
- // Only process state events from uids in AID_* and packages that are whitelisted in
- // mAllowedPkg.
- // Whitelisted AIDs are AID_ROOT and all AIDs in [1000, 2000)
- if (event.GetUid() == AID_ROOT || (event.GetUid() >= 1000 && event.GetUid() < 2000) ||
- mAllowedLogSources.find(event.GetUid()) != mAllowedLogSources.end()) {
- if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
- mStateTrackers[event.GetTagId()]->onLogEvent(event);
- }
- }
-}
-
-void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
- // Check if state tracker already exists.
- if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
- mStateTrackers[atomId] = new StateTracker(atomId);
- }
- mStateTrackers[atomId]->registerListener(listener);
-}
-
-void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
- std::unique_lock<std::mutex> lock(mMutex);
-
- // Hold the sp<> until the lock is released so that ~StateTracker() is
- // not called while the lock is held.
- sp<StateTracker> toRemove;
-
- // Unregister listener from correct StateTracker
- auto it = mStateTrackers.find(atomId);
- if (it != mStateTrackers.end()) {
- it->second->unregisterListener(listener);
-
- // Remove the StateTracker if it has no listeners
- if (it->second->getListenersCount() == 0) {
- toRemove = it->second;
- mStateTrackers.erase(it);
- }
- } else {
- ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
- atomId);
- }
- lock.unlock();
-}
-
-bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key,
- FieldValue* output) const {
- auto it = mStateTrackers.find(atomId);
- if (it != mStateTrackers.end()) {
- return it->second->getStateValue(key, output);
- }
- return false;
-}
-
-void StateManager::updateLogSources(const sp<UidMap>& uidMap) {
- mAllowedLogSources.clear();
- for (const auto& pkg : mAllowedPkg) {
- auto uids = uidMap->getAppUid(pkg);
- mAllowedLogSources.insert(uids.begin(), uids.end());
- }
-}
-
-void StateManager::notifyAppChanged(const string& apk, const sp<UidMap>& uidMap) {
- if (mAllowedPkg.find(apk) != mAllowedPkg.end()) {
- updateLogSources(uidMap);
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
deleted file mode 100644
index 18c404c29c4e..000000000000
--- a/cmds/statsd/src/state/StateManager.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <inttypes.h>
-#include <utils/RefBase.h>
-
-#include <set>
-#include <string>
-#include <unordered_map>
-
-#include "HashableDimensionKey.h"
-#include "packages/UidMap.h"
-#include "state/StateListener.h"
-#include "state/StateTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * This class is NOT thread safe.
- * It should only be used while StatsLogProcessor's lock is held.
- */
-class StateManager : public virtual RefBase {
-public:
- StateManager();
-
- ~StateManager(){};
-
- // Returns a pointer to the single, shared StateManager object.
- static StateManager& getInstance();
-
- // Unregisters all listeners and removes all trackers from StateManager.
- void clear();
-
- // Notifies the correct StateTracker of an event.
- void onLogEvent(const LogEvent& event);
-
- // Notifies the StateTracker for the given atomId to register listener.
- // If the correct StateTracker does not exist, a new StateTracker is created.
- // Note: StateTrackers can be created for non-state atoms. They are essentially empty and
- // do not perform any actions.
- void registerListener(const int32_t 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(const int32_t atomId, wp<StateListener> listener);
-
- // Returns true if the StateTracker exists and queries for the
- // original state value mapped to the given query key. The state value is
- // stored and output in a FieldValue class.
- // Returns false if the StateTracker doesn't exist.
- bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
- FieldValue* output) const;
-
- // Updates mAllowedLogSources with the latest uids for the packages that are allowed to log.
- void updateLogSources(const sp<UidMap>& uidMap);
-
- void notifyAppChanged(const string& apk, const sp<UidMap>& uidMap);
-
- inline int getStateTrackersCount() const {
- return mStateTrackers.size();
- }
-
- inline int getListenersCount(const int32_t atomId) const {
- auto it = mStateTrackers.find(atomId);
- if (it != mStateTrackers.end()) {
- return it->second->getListenersCount();
- }
- return -1;
- }
-
-private:
- mutable std::mutex mMutex;
-
- // Maps state atom ids to StateTrackers
- std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers;
-
- // The package names that can log state events.
- const std::set<std::string> mAllowedPkg;
-
- // The combined uid sources (after translating pkg name to uid).
- // State events from uids that are not in the list will be ignored to avoid state pollution.
- std::set<int32_t> mAllowedLogSources;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
deleted file mode 100644
index 41e525c343ba..000000000000
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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.
- */
-
-#define DEBUG true // STOPSHIP if true
-#include "Log.h"
-
-#include "stats_util.h"
-
-#include "StateTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
-}
-
-void StateTracker::onLogEvent(const LogEvent& event) {
- const int64_t eventTimeNs = event.GetElapsedTimestampNs();
-
- // Parse event for primary field values i.e. primary key.
- HashableDimensionKey primaryKey;
- filterPrimaryKey(event.getValues(), &primaryKey);
-
- FieldValue newState;
- if (!getStateFieldValueFromLogEvent(event, &newState)) {
- ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
- clearStateForPrimaryKey(eventTimeNs, primaryKey);
- return;
- }
-
- mField.setField(newState.mField.getField());
-
- if (newState.mValue.getType() != INT) {
- ALOGE("StateTracker error extracting state from log event. Type: %d",
- newState.mValue.getType());
- clearStateForPrimaryKey(eventTimeNs, primaryKey);
- return;
- }
-
- if (int resetState = event.getResetState(); resetState != -1) {
- VLOG("StateTracker new reset state: %d", resetState);
- const FieldValue resetStateFieldValue(mField, Value(resetState));
- handleReset(eventTimeNs, resetStateFieldValue);
- return;
- }
-
- const bool nested = newState.mAnnotations.isNested();
- StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
- updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
-}
-
-void StateTracker::registerListener(wp<StateListener> listener) {
- mListeners.insert(listener);
-}
-
-void StateTracker::unregisterListener(wp<StateListener> listener) {
- mListeners.erase(listener);
-}
-
-bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
- output->mField = mField;
-
- if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
- output->mValue = it->second.state;
- return true;
- }
-
- // Set the state value to kStateUnknown if query key is not found in state map.
- output->mValue = kStateUnknown;
- return false;
-}
-
-void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) {
- VLOG("StateTracker handle reset");
- for (auto& [primaryKey, stateValueInfo] : mStateMap) {
- updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
- false /* nested; treat this state change as not nested */,
- &stateValueInfo);
- }
-}
-
-void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
- const HashableDimensionKey& primaryKey) {
- VLOG("StateTracker clear state for primary key");
- const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
- mStateMap.find(primaryKey);
-
- // If there is no entry for the primaryKey in mStateMap, then the state is already
- // kStateUnknown.
- const FieldValue state(mField, Value(kStateUnknown));
- if (it != mStateMap.end()) {
- updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
- false /* nested; treat this state change as not nested */,
- &it->second);
- }
-}
-
-void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
- const HashableDimensionKey& primaryKey,
- const FieldValue& newState, const bool nested,
- StateValueInfo* stateValueInfo) {
- FieldValue oldState;
- oldState.mField = mField;
- oldState.mValue.setInt(stateValueInfo->state);
- const int32_t oldStateValue = stateValueInfo->state;
- const int32_t newStateValue = newState.mValue.int_value;
-
- if (kStateUnknown == newStateValue) {
- mStateMap.erase(primaryKey);
- }
-
- // Update state map for non-nested counting case.
- // Every state event triggers a state overwrite.
- if (!nested) {
- stateValueInfo->state = newStateValue;
- stateValueInfo->count = 1;
-
- // Notify listeners if state has changed.
- if (oldStateValue != newStateValue) {
- notifyListeners(eventTimeNs, primaryKey, oldState, newState);
- }
- return;
- }
-
- // Update state map for nested counting case.
- //
- // Nested counting is only allowed for binary state events such as ON/OFF or
- // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
- // events: ON, ON, OFF. The state will still be ON until we see the same
- // number of OFF events as ON events.
- //
- // In atoms.proto, a state atom with nested counting enabled
- // must only have 2 states. There is no enforcemnt here of this requirement.
- // The atom must be logged correctly.
- if (kStateUnknown == newStateValue) {
- if (kStateUnknown != oldStateValue) {
- notifyListeners(eventTimeNs, primaryKey, oldState, newState);
- }
- } else if (oldStateValue == kStateUnknown) {
- stateValueInfo->state = newStateValue;
- stateValueInfo->count = 1;
- notifyListeners(eventTimeNs, primaryKey, oldState, newState);
- } else if (oldStateValue == newStateValue) {
- stateValueInfo->count++;
- } else if (--stateValueInfo->count == 0) {
- stateValueInfo->state = newStateValue;
- stateValueInfo->count = 1;
- notifyListeners(eventTimeNs, primaryKey, oldState, newState);
- }
-}
-
-void StateTracker::notifyListeners(const int64_t eventTimeNs,
- const HashableDimensionKey& primaryKey,
- const FieldValue& oldState, const FieldValue& newState) {
- for (auto l : mListeners) {
- auto sl = l.promote();
- if (sl != nullptr) {
- sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
- }
- }
-}
-
-bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
- const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
- if (-1 == exclusiveStateFieldIndex) {
- ALOGE("error extracting state from log event. Missing exclusive state field.");
- return false;
- }
-
- *output = event.getValues()[exclusiveStateFieldIndex];
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
deleted file mode 100644
index abd579e7e302..000000000000
--- a/cmds/statsd/src/state/StateTracker.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <utils/RefBase.h>
-#include "HashableDimensionKey.h"
-#include "logd/LogEvent.h"
-
-#include "state/StateListener.h"
-
-#include <unordered_map>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StateTracker : public virtual RefBase {
-public:
- StateTracker(const int32_t atomId);
-
- virtual ~StateTracker(){};
-
- // Updates state map and notifies all listeners if a state change occurs.
- // Checks if a state change has occurred by getting the state value from
- // the log event and comparing the old and new states.
- void onLogEvent(const LogEvent& event);
-
- // Adds new listeners to set of StateListeners. If a listener is already
- // registered, it is ignored.
- void registerListener(wp<StateListener> listener);
-
- void unregisterListener(wp<StateListener> listener);
-
- // The output is a FieldValue object that has mStateField as the field and
- // the original state value (found using the given query key) as the value.
- //
- // If the key isn't mapped to a state or the key size doesn't match the
- // number of primary fields, the output value is set to kStateUnknown.
- bool getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const;
-
- inline int getListenersCount() const {
- return mListeners.size();
- }
-
- const static int kStateUnknown = -1;
-
-private:
- struct StateValueInfo {
- int32_t state = kStateUnknown; // state value
- int count = 0; // nested count (only used for binary states)
- };
-
- Field mField;
-
- // Maps primary key to state value info
- std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
-
- // Set of all StateListeners (objects listening for state changes)
- std::set<wp<StateListener>> mListeners;
-
- // Reset all state values in map to the given state.
- void handleReset(const int64_t eventTimeNs, const FieldValue& newState);
-
- // Clears the state value mapped to the given primary key by setting it to kStateUnknown.
- void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
-
- // Update the StateMap based on the received state value.
- void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
- const FieldValue& newState, const bool nested,
- StateValueInfo* stateValueInfo);
-
- // Notify registered state listeners of state change.
- void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
- const FieldValue& oldState, const FieldValue& newState);
-};
-
-bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
deleted file mode 100644
index bb0796326f01..000000000000
--- a/cmds/statsd/src/stats_log.proto
+++ /dev/null
@@ -1,553 +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.
- */
-
-syntax = "proto2";
-
-package android.os.statsd;
-
-option java_package = "com.android.os";
-option java_outer_classname = "StatsLog";
-
-import "frameworks/proto_logging/stats/atoms.proto";
-
-message DimensionsValue {
- optional int32 field = 1;
-
- oneof value {
- string value_str = 2;
- int32 value_int = 3;
- int64 value_long = 4;
- bool value_bool = 5;
- float value_float = 6;
- DimensionsValueTuple value_tuple = 7;
- uint64 value_str_hash = 8;
- }
-}
-
-message DimensionsValueTuple {
- repeated DimensionsValue dimensions_value = 1;
-}
-
-message StateValue {
- optional int32 atom_id = 1;
-
- oneof contents {
- int64 group_id = 2;
- int32 value = 3;
- }
-}
-
-message EventMetricData {
- optional int64 elapsed_timestamp_nanos = 1;
-
- optional Atom atom = 2;
-
- optional int64 wall_clock_timestamp_nanos = 3 [deprecated = true];
-}
-
-message CountBucketInfo {
- optional int64 start_bucket_elapsed_nanos = 1;
-
- optional int64 end_bucket_elapsed_nanos = 2;
-
- optional int64 count = 3;
-
- optional int64 bucket_num = 4;
-
- optional int64 start_bucket_elapsed_millis = 5;
-
- optional int64 end_bucket_elapsed_millis = 6;
-}
-
-message CountMetricData {
- optional DimensionsValue dimensions_in_what = 1;
-
- repeated StateValue slice_by_state = 6;
-
- repeated CountBucketInfo bucket_info = 3;
-
- repeated DimensionsValue dimension_leaf_values_in_what = 4;
-
- optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
-
- repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
-}
-
-message DurationBucketInfo {
- optional int64 start_bucket_elapsed_nanos = 1;
-
- optional int64 end_bucket_elapsed_nanos = 2;
-
- optional int64 duration_nanos = 3;
-
- optional int64 bucket_num = 4;
-
- optional int64 start_bucket_elapsed_millis = 5;
-
- optional int64 end_bucket_elapsed_millis = 6;
-}
-
-message DurationMetricData {
- optional DimensionsValue dimensions_in_what = 1;
-
- repeated StateValue slice_by_state = 6;
-
- repeated DurationBucketInfo bucket_info = 3;
-
- repeated DimensionsValue dimension_leaf_values_in_what = 4;
-
- optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
-
- repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
-}
-
-message ValueBucketInfo {
- optional int64 start_bucket_elapsed_nanos = 1;
-
- optional int64 end_bucket_elapsed_nanos = 2;
-
- optional int64 value = 3 [deprecated = true];
-
- oneof single_value {
- int64 value_long = 7 [deprecated = true];
-
- double value_double = 8 [deprecated = true];
- }
-
- message Value {
- optional int32 index = 1;
- oneof value {
- int64 value_long = 2;
- double value_double = 3;
- }
- }
-
- repeated Value values = 9;
-
- optional int64 bucket_num = 4;
-
- optional int64 start_bucket_elapsed_millis = 5;
-
- optional int64 end_bucket_elapsed_millis = 6;
-
- optional int64 condition_true_nanos = 10;
-}
-
-message ValueMetricData {
- optional DimensionsValue dimensions_in_what = 1;
-
- repeated StateValue slice_by_state = 6;
-
- repeated ValueBucketInfo bucket_info = 3;
-
- repeated DimensionsValue dimension_leaf_values_in_what = 4;
-
- optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
-
- repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
-}
-
-message GaugeBucketInfo {
- optional int64 start_bucket_elapsed_nanos = 1;
-
- optional int64 end_bucket_elapsed_nanos = 2;
-
- repeated Atom atom = 3;
-
- repeated int64 elapsed_timestamp_nanos = 4;
-
- repeated int64 wall_clock_timestamp_nanos = 5 [deprecated = true];
-
- optional int64 bucket_num = 6;
-
- optional int64 start_bucket_elapsed_millis = 7;
-
- optional int64 end_bucket_elapsed_millis = 8;
-}
-
-message GaugeMetricData {
- optional DimensionsValue dimensions_in_what = 1;
-
- // Currently unsupported
- repeated StateValue slice_by_state = 6;
-
- repeated GaugeBucketInfo bucket_info = 3;
-
- repeated DimensionsValue dimension_leaf_values_in_what = 4;
-
- optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
-
- repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
-}
-
-message StatsLogReport {
- optional int64 metric_id = 1;
-
- // Fields 2 and 3 are reserved.
-
- // Keep this in sync with BucketDropReason enum in MetricProducer.h.
- enum BucketDropReason {
- // For ValueMetric, a bucket is dropped during a dump report request iff
- // current bucket should be included, a pull is needed (pulled metric and
- // condition is true), and we are under fast time constraints.
- DUMP_REPORT_REQUESTED = 1;
- EVENT_IN_WRONG_BUCKET = 2;
- CONDITION_UNKNOWN = 3;
- PULL_FAILED = 4;
- PULL_DELAYED = 5;
- DIMENSION_GUARDRAIL_REACHED = 6;
- MULTIPLE_BUCKETS_SKIPPED = 7;
- // Not an invalid bucket case, but the bucket is dropped.
- BUCKET_TOO_SMALL = 8;
- // Not an invalid bucket case, but the bucket is skipped.
- NO_DATA = 9;
- };
-
- message DropEvent {
- optional BucketDropReason drop_reason = 1;
-
- optional int64 drop_time_millis = 2;
- }
-
- message SkippedBuckets {
- optional int64 start_bucket_elapsed_nanos = 1;
-
- optional int64 end_bucket_elapsed_nanos = 2;
-
- optional int64 start_bucket_elapsed_millis = 3;
-
- optional int64 end_bucket_elapsed_millis = 4;
-
- // The number of drop events is capped by StatsdStats::kMaxLoggedBucketDropEvents.
- // The current maximum is 10 drop events.
- repeated DropEvent drop_event = 5;
- }
-
- message EventMetricDataWrapper {
- repeated EventMetricData data = 1;
- }
- message CountMetricDataWrapper {
- repeated CountMetricData data = 1;
- }
- message DurationMetricDataWrapper {
- repeated DurationMetricData data = 1;
- }
- message ValueMetricDataWrapper {
- repeated ValueMetricData data = 1;
- repeated SkippedBuckets skipped = 2;
- }
-
- message GaugeMetricDataWrapper {
- repeated GaugeMetricData data = 1;
- repeated SkippedBuckets skipped = 2;
- }
-
- oneof data {
- EventMetricDataWrapper event_metrics = 4;
- CountMetricDataWrapper count_metrics = 5;
- DurationMetricDataWrapper duration_metrics = 6;
- ValueMetricDataWrapper value_metrics = 7;
- GaugeMetricDataWrapper gauge_metrics = 8;
- }
-
- optional int64 time_base_elapsed_nano_seconds = 9;
-
- optional int64 bucket_size_nano_seconds = 10;
-
- optional DimensionsValue dimensions_path_in_what = 11;
-
- optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true];
-
- // DO NOT USE field 13.
-
- optional bool is_active = 14;
-}
-
-message UidMapping {
- message PackageInfoSnapshot {
- message PackageInfo {
- optional string name = 1;
-
- optional int64 version = 2;
-
- optional int32 uid = 3;
-
- optional bool deleted = 4;
-
- optional uint64 name_hash = 5;
-
- optional string version_string = 6;
-
- optional uint64 version_string_hash = 7;
-
- optional string installer = 8;
-
- optional uint64 installer_hash = 9;
- }
- optional int64 elapsed_timestamp_nanos = 1;
-
- repeated PackageInfo package_info = 2;
- }
- repeated PackageInfoSnapshot snapshots = 1;
-
- message Change {
- optional bool deletion = 1;
-
- optional int64 elapsed_timestamp_nanos = 2;
- optional string app = 3;
- optional int32 uid = 4;
-
- optional int64 new_version = 5;
- optional int64 prev_version = 6;
- optional uint64 app_hash = 7;
- optional string new_version_string = 8;
- optional string prev_version_string = 9;
- optional uint64 new_version_string_hash = 10;
- optional uint64 prev_version_string_hash = 11;
- }
- repeated Change changes = 2;
-}
-
-message ConfigMetricsReport {
- repeated StatsLogReport metrics = 1;
-
- optional UidMapping uid_map = 2;
-
- optional int64 last_report_elapsed_nanos = 3;
-
- optional int64 current_report_elapsed_nanos = 4;
-
- optional int64 last_report_wall_clock_nanos = 5;
-
- optional int64 current_report_wall_clock_nanos = 6;
-
- message Annotation {
- optional int64 field_int64 = 1;
- optional int32 field_int32 = 2;
- }
- repeated Annotation annotation = 7;
-
- enum DumpReportReason {
- DEVICE_SHUTDOWN = 1;
- CONFIG_UPDATED = 2;
- CONFIG_REMOVED = 3;
- GET_DATA_CALLED = 4;
- ADB_DUMP = 5;
- CONFIG_RESET = 6;
- STATSCOMPANION_DIED = 7;
- TERMINATION_SIGNAL_RECEIVED = 8;
- }
- optional DumpReportReason dump_report_reason = 8;
-
- repeated string strings = 9;
-}
-
-message ConfigMetricsReportList {
- message ConfigKey {
- optional int32 uid = 1;
- optional int64 id = 2;
- }
- optional ConfigKey config_key = 1;
-
- repeated ConfigMetricsReport reports = 2;
-
- reserved 10;
-}
-
-message StatsdStatsReport {
- optional int32 stats_begin_time_sec = 1;
-
- optional int32 stats_end_time_sec = 2;
-
- message MatcherStats {
- optional int64 id = 1;
- optional int32 matched_times = 2;
- }
-
- message ConditionStats {
- optional int64 id = 1;
- optional int32 max_tuple_counts = 2;
- }
-
- message MetricStats {
- optional int64 id = 1;
- optional int32 max_tuple_counts = 2;
- }
-
- message AlertStats {
- optional int64 id = 1;
- optional int32 alerted_times = 2;
- }
-
- message ConfigStats {
- optional int32 uid = 1;
- optional int64 id = 2;
- optional int32 creation_time_sec = 3;
- optional int32 deletion_time_sec = 4;
- optional int32 reset_time_sec = 19;
- optional int32 metric_count = 5;
- optional int32 condition_count = 6;
- optional int32 matcher_count = 7;
- optional int32 alert_count = 8;
- optional bool is_valid = 9;
- repeated int32 broadcast_sent_time_sec = 10;
- repeated int32 data_drop_time_sec = 11;
- repeated int64 data_drop_bytes = 21;
- repeated int32 dump_report_time_sec = 12;
- repeated int32 dump_report_data_size = 20;
- repeated MatcherStats matcher_stats = 13;
- repeated ConditionStats condition_stats = 14;
- repeated MetricStats metric_stats = 15;
- repeated AlertStats alert_stats = 16;
- repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true];
- message Annotation {
- optional int64 field_int64 = 1;
- optional int32 field_int32 = 2;
- }
- repeated Annotation annotation = 18;
- repeated int32 activation_time_sec = 22;
- repeated int32 deactivation_time_sec = 23;
- }
-
- repeated ConfigStats config_stats = 3;
-
- message AtomStats {
- optional int32 tag = 1;
- optional int32 count = 2;
- optional int32 error_count = 3;
- }
-
- repeated AtomStats atom_stats = 7;
-
- message UidMapStats {
- optional int32 changes = 1;
- optional int32 bytes_used = 2;
- optional int32 dropped_changes = 3;
- optional int32 deleted_apps = 4;
- }
- optional UidMapStats uidmap_stats = 8;
-
- message AnomalyAlarmStats {
- optional int32 alarms_registered = 1;
- }
- optional AnomalyAlarmStats anomaly_alarm_stats = 9;
-
- message PulledAtomStats {
- optional int32 atom_id = 1;
- optional int64 total_pull = 2;
- optional int64 total_pull_from_cache = 3;
- optional int64 min_pull_interval_sec = 4;
- optional int64 average_pull_time_nanos = 5;
- optional int64 max_pull_time_nanos = 6;
- optional int64 average_pull_delay_nanos = 7;
- optional int64 max_pull_delay_nanos = 8;
- optional int64 data_error = 9;
- optional int64 pull_timeout = 10;
- optional int64 pull_exceed_max_delay = 11;
- optional int64 pull_failed = 12;
- optional int64 stats_companion_pull_failed = 13 [deprecated = true];
- optional int64 stats_companion_pull_binder_transaction_failed = 14 [deprecated = true];
- optional int64 empty_data = 15;
- optional int64 registered_count = 16;
- optional int64 unregistered_count = 17;
- optional int32 atom_error_count = 18;
- optional int64 binder_call_failed = 19;
- optional int64 failed_uid_provider_not_found = 20;
- optional int64 puller_not_found = 21;
- message PullTimeoutMetadata {
- optional int64 pull_timeout_uptime_millis = 1;
- optional int64 pull_timeout_elapsed_millis = 2;
- }
- repeated PullTimeoutMetadata pull_atom_metadata = 22;
- }
- repeated PulledAtomStats pulled_atom_stats = 10;
-
- message AtomMetricStats {
- optional int64 metric_id = 1;
- optional int64 hard_dimension_limit_reached = 2;
- optional int64 late_log_event_skipped = 3;
- optional int64 skipped_forward_buckets = 4;
- optional int64 bad_value_type = 5;
- optional int64 condition_change_in_next_bucket = 6;
- optional int64 invalidated_bucket = 7;
- optional int64 bucket_dropped = 8;
- optional int64 min_bucket_boundary_delay_ns = 9;
- optional int64 max_bucket_boundary_delay_ns = 10;
- optional int64 bucket_unknown_condition = 11;
- optional int64 bucket_count = 12;
- }
- repeated AtomMetricStats atom_metric_stats = 17;
-
- message LoggerErrorStats {
- optional int32 logger_disconnection_sec = 1;
- optional int32 error_code = 2;
- }
- repeated LoggerErrorStats logger_error_stats = 11;
-
- message PeriodicAlarmStats {
- optional int32 alarms_registered = 1;
- }
- optional PeriodicAlarmStats periodic_alarm_stats = 12;
-
- message SkippedLogEventStats {
- optional int32 tag = 1;
- optional int64 elapsed_timestamp_nanos = 2;
- }
- repeated SkippedLogEventStats skipped_log_event_stats = 13;
-
- repeated int64 log_loss_stats = 14;
-
- repeated int32 system_restart_sec = 15;
-
- message LogLossStats {
- optional int32 detected_time_sec = 1;
- optional int32 count = 2;
- optional int32 last_error = 3;
- optional int32 last_tag = 4;
- optional int32 uid = 5;
- optional int32 pid = 6;
- }
- repeated LogLossStats detected_log_loss = 16;
-
- message EventQueueOverflow {
- optional int32 count = 1;
- optional int64 max_queue_history_ns = 2;
- optional int64 min_queue_history_ns = 3;
- }
-
- optional EventQueueOverflow queue_overflow = 18;
-
- message ActivationBroadcastGuardrail {
- optional int32 uid = 1;
- repeated int32 guardrail_met_sec = 2;
- }
-
- repeated ActivationBroadcastGuardrail activation_guardrail_stats = 19;
-}
-
-message AlertTriggerDetails {
- message MetricValue {
- optional int64 metric_id = 1;
- optional DimensionsValue dimension_in_what = 2;
- optional DimensionsValue dimension_in_condition = 3 [deprecated = true];
- optional int64 value = 4;
- }
- oneof value {
- MetricValue trigger_metric = 1;
- EventMetricData trigger_event = 2;
- }
- optional UidMapping.PackageInfoSnapshot package_info = 3;
-}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
deleted file mode 100644
index 423bae8bc0a4..000000000000
--- a/cmds/statsd/src/stats_log_util.cpp
+++ /dev/null
@@ -1,609 +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.
- */
-
-#include "hash.h"
-#include "stats_log_util.h"
-
-#include <aidl/android/os/IStatsCompanionService.h>
-#include <private/android_filesystem_config.h>
-#include <set>
-#include <utils/SystemClock.h>
-
-#include "statscompanion_util.h"
-
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_BOOL;
-using android::util::FIELD_TYPE_FIXED64;
-using android::util::FIELD_TYPE_FLOAT;
-using android::util::FIELD_TYPE_INT32;
-using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_MESSAGE;
-using android::util::FIELD_TYPE_STRING;
-using android::util::FIELD_TYPE_UINT64;
-using android::util::ProtoOutputStream;
-
-using aidl::android::os::IStatsCompanionService;
-using std::shared_ptr;
-using std::string;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// for DimensionsValue Proto
-const int DIMENSIONS_VALUE_FIELD = 1;
-const int DIMENSIONS_VALUE_VALUE_STR = 2;
-const int DIMENSIONS_VALUE_VALUE_INT = 3;
-const int DIMENSIONS_VALUE_VALUE_LONG = 4;
-// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type.
-const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
-const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
-const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8;
-
-const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
-
-// for StateValue Proto
-const int STATE_VALUE_ATOM_ID = 1;
-const int STATE_VALUE_CONTENTS_GROUP_ID = 2;
-const int STATE_VALUE_CONTENTS_VALUE = 3;
-
-// for PulledAtomStats proto
-const int FIELD_ID_PULLED_ATOM_STATS = 10;
-const int FIELD_ID_PULL_ATOM_ID = 1;
-const int FIELD_ID_TOTAL_PULL = 2;
-const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3;
-const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4;
-const int FIELD_ID_AVERAGE_PULL_TIME_NANOS = 5;
-const int FIELD_ID_MAX_PULL_TIME_NANOS = 6;
-const int FIELD_ID_AVERAGE_PULL_DELAY_NANOS = 7;
-const int FIELD_ID_MAX_PULL_DELAY_NANOS = 8;
-const int FIELD_ID_DATA_ERROR = 9;
-const int FIELD_ID_PULL_TIMEOUT = 10;
-const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
-const int FIELD_ID_PULL_FAILED = 12;
-const int FIELD_ID_EMPTY_DATA = 15;
-const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
-const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
-const int FIELD_ID_ATOM_ERROR_COUNT = 18;
-const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19;
-const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20;
-const int FIELD_ID_PULLER_NOT_FOUND = 21;
-const int FIELD_ID_PULL_TIMEOUT_METADATA = 22;
-const int FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS = 1;
-const int FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS = 2;
-
-// for AtomMetricStats proto
-const int FIELD_ID_ATOM_METRIC_STATS = 17;
-const int FIELD_ID_METRIC_ID = 1;
-const int FIELD_ID_HARD_DIMENSION_LIMIT_REACHED = 2;
-const int FIELD_ID_LATE_LOG_EVENT_SKIPPED = 3;
-const int FIELD_ID_SKIPPED_FORWARD_BUCKETS = 4;
-const int FIELD_ID_BAD_VALUE_TYPE = 5;
-const int FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET = 6;
-const int FIELD_ID_INVALIDATED_BUCKET = 7;
-const int FIELD_ID_BUCKET_DROPPED = 8;
-const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9;
-const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10;
-const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11;
-const int FIELD_ID_BUCKET_COUNT = 12;
-
-namespace {
-
-void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* index, int depth,
- int prefix, std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- size_t count = dims.size();
- while (*index < count) {
- const auto& dim = dims[*index];
- const int valueDepth = dim.mField.getDepth();
- const int valuePrefix = dim.mField.getPrefix(depth);
- const int fieldNum = dim.mField.getPosAtDepth(depth);
- if (valueDepth > 2) {
- ALOGE("Depth > 2 not supported");
- return;
- }
-
- if (depth == valueDepth && valuePrefix == prefix) {
- uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- DIMENSIONS_VALUE_TUPLE_VALUE);
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
- switch (dim.mValue.getType()) {
- case INT:
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
- dim.mValue.int_value);
- break;
- case LONG:
- protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
- (long long)dim.mValue.long_value);
- break;
- case FLOAT:
- protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
- dim.mValue.float_value);
- break;
- case STRING:
- if (str_set == nullptr) {
- protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
- dim.mValue.str_value);
- } else {
- str_set->insert(dim.mValue.str_value);
- protoOutput->write(
- FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
- (long long)Hash64(dim.mValue.str_value));
- }
- break;
- default:
- break;
- }
- if (token != 0) {
- protoOutput->end(token);
- }
- (*index)++;
- } else if (valueDepth > depth && valuePrefix == prefix) {
- // Writing the sub tree
- uint64_t dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
- uint64_t tupleToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
- writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth),
- str_set, protoOutput);
- protoOutput->end(tupleToken);
- protoOutput->end(dimensionToken);
- } else {
- // Done with the prev sub tree
- return;
- }
- }
-}
-
-void writeDimensionLeafToProtoHelper(const std::vector<FieldValue>& dims,
- const int dimensionLeafField,
- size_t* index, int depth,
- int prefix, std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- size_t count = dims.size();
- while (*index < count) {
- const auto& dim = dims[*index];
- const int valueDepth = dim.mField.getDepth();
- const int valuePrefix = dim.mField.getPrefix(depth);
- if (valueDepth > 2) {
- ALOGE("Depth > 2 not supported");
- return;
- }
-
- if (depth == valueDepth && valuePrefix == prefix) {
- uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- dimensionLeafField);
- switch (dim.mValue.getType()) {
- case INT:
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
- dim.mValue.int_value);
- break;
- case LONG:
- protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
- (long long)dim.mValue.long_value);
- break;
- case FLOAT:
- protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
- dim.mValue.float_value);
- break;
- case STRING:
- if (str_set == nullptr) {
- protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
- dim.mValue.str_value);
- } else {
- str_set->insert(dim.mValue.str_value);
- protoOutput->write(
- FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
- (long long)Hash64(dim.mValue.str_value));
- }
- break;
- default:
- break;
- }
- if (token != 0) {
- protoOutput->end(token);
- }
- (*index)++;
- } else if (valueDepth > depth && valuePrefix == prefix) {
- writeDimensionLeafToProtoHelper(dims, dimensionLeafField,
- index, valueDepth, dim.mField.getPrefix(valueDepth),
- str_set, protoOutput);
- } else {
- // Done with the prev sub tree
- return;
- }
- }
-}
-
-void writeDimensionPathToProtoHelper(const std::vector<Matcher>& fieldMatchers,
- size_t* index, int depth, int prefix,
- ProtoOutputStream* protoOutput) {
- size_t count = fieldMatchers.size();
- while (*index < count) {
- const Field& field = fieldMatchers[*index].mMatcher;
- const int valueDepth = field.getDepth();
- const int valuePrefix = field.getPrefix(depth);
- const int fieldNum = field.getPosAtDepth(depth);
- if (valueDepth > 2) {
- ALOGE("Depth > 2 not supported");
- return;
- }
-
- if (depth == valueDepth && valuePrefix == prefix) {
- uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
- DIMENSIONS_VALUE_TUPLE_VALUE);
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
- if (token != 0) {
- protoOutput->end(token);
- }
- (*index)++;
- } else if (valueDepth > depth && valuePrefix == prefix) {
- // Writing the sub tree
- uint64_t dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
- uint64_t tupleToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
- writeDimensionPathToProtoHelper(fieldMatchers, index, valueDepth,
- field.getPrefix(valueDepth), protoOutput);
- protoOutput->end(tupleToken);
- protoOutput->end(dimensionToken);
- } else {
- // Done with the prev sub tree
- return;
- }
- }
-}
-
-} // namespace
-
-void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- if (dimension.getValues().size() == 0) {
- return;
- }
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
- dimension.getValues()[0].mField.getTag());
- uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
- size_t index = 0;
- writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, str_set, protoOutput);
- protoOutput->end(topToken);
-}
-
-void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
- const int dimensionLeafFieldId,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput) {
- if (dimension.getValues().size() == 0) {
- return;
- }
- size_t index = 0;
- writeDimensionLeafToProtoHelper(dimension.getValues(), dimensionLeafFieldId,
- &index, 0, 0, str_set, protoOutput);
-}
-
-void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
- ProtoOutputStream* protoOutput) {
- if (fieldMatchers.size() == 0) {
- return;
- }
- protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
- fieldMatchers[0].mMatcher.getTag());
- uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
- size_t index = 0;
- writeDimensionPathToProtoHelper(fieldMatchers, &index, 0, 0, protoOutput);
- protoOutput->end(topToken);
-}
-
-// Supported Atoms format
-// XYZ_Atom {
-// repeated SubMsg field_1 = 1;
-// SubMsg2 field_2 = 2;
-// int32/float/string/int63 field_3 = 3;
-// }
-// logd's msg format, doesn't allow us to distinguish between the 2 cases below
-// Case (1):
-// Atom {
-// SubMsg {
-// int i = 1;
-// int j = 2;
-// }
-// repeated SubMsg
-// }
-//
-// and case (2):
-// Atom {
-// SubMsg {
-// repeated int i = 1;
-// repeated int j = 2;
-// }
-// optional SubMsg = 1;
-// }
-//
-//
-void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>& dims,
- size_t* index, int depth, int prefix,
- ProtoOutputStream* protoOutput) {
- size_t count = dims.size();
- while (*index < count) {
- const auto& dim = dims[*index];
- const int valueDepth = dim.mField.getDepth();
- const int valuePrefix = dim.mField.getPrefix(depth);
- const int fieldNum = dim.mField.getPosAtDepth(depth);
- if (valueDepth > 2) {
- ALOGE("Depth > 2 not supported");
- return;
- }
-
- if (depth == valueDepth && valuePrefix == prefix) {
- switch (dim.mValue.getType()) {
- case INT:
- protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value);
- break;
- case LONG:
- protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
- (long long)dim.mValue.long_value);
- break;
- case FLOAT:
- protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
- break;
- case STRING: {
- protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
- break;
- }
- case STORAGE:
- protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
- (const char*)dim.mValue.storage_value.data(),
- dim.mValue.storage_value.size());
- break;
- default:
- break;
- }
- (*index)++;
- } else if (valueDepth > depth && valuePrefix == prefix) {
- // Writing the sub tree
- uint64_t msg_token = 0ULL;
- if (valueDepth == depth + 2) {
- msg_token =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
- } else if (valueDepth == depth + 1) {
- msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
- }
- // Directly jump to the leaf value because the repeated position field is implied
- // by the position of the sub msg in the parent field.
- writeFieldValueTreeToStreamHelper(tagId, dims, index, valueDepth,
- dim.mField.getPrefix(valueDepth), protoOutput);
- if (msg_token != 0) {
- protoOutput->end(msg_token);
- }
- } else {
- // Done with the prev sub tree
- return;
- }
- }
-}
-
-void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
- util::ProtoOutputStream* protoOutput) {
- uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId);
-
- size_t index = 0;
- writeFieldValueTreeToStreamHelper(tagId, values, &index, 0, 0, protoOutput);
- protoOutput->end(atomToken);
-}
-
-void writeStateToProto(const FieldValue& state, util::ProtoOutputStream* protoOutput) {
- protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_ATOM_ID, state.mField.getTag());
-
- switch (state.mValue.getType()) {
- case INT:
- protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_CONTENTS_VALUE,
- state.mValue.int_value);
- break;
- case LONG:
- protoOutput->write(FIELD_TYPE_INT64 | STATE_VALUE_CONTENTS_GROUP_ID,
- state.mValue.long_value);
- break;
- default:
- break;
- }
-}
-
-int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
- int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
- if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
- uid != AID_ROOT) {
- bucketSizeMillis = 5 * 60 * 1000LL;
- }
- return bucketSizeMillis;
-}
-
-int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
- switch (unit) {
- case ONE_MINUTE:
- return 60 * 1000LL;
- case FIVE_MINUTES:
- return 5 * 60 * 1000LL;
- case TEN_MINUTES:
- return 10 * 60 * 1000LL;
- case THIRTY_MINUTES:
- return 30 * 60 * 1000LL;
- case ONE_HOUR:
- return 60 * 60 * 1000LL;
- case THREE_HOURS:
- return 3 * 60 * 60 * 1000LL;
- case SIX_HOURS:
- return 6 * 60 * 60 * 1000LL;
- case TWELVE_HOURS:
- 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:
- default:
- return -1;
- }
-}
-
-void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
- util::ProtoOutputStream* protoOutput) {
- uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS |
- FIELD_COUNT_REPEATED);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL_FROM_CACHE,
- (long long)pair.second.totalPullFromCache);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC,
- (long long)pair.second.minPullIntervalSec);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_TIME_NANOS,
- (long long)pair.second.avgPullTimeNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_TIME_NANOS,
- (long long)pair.second.maxPullTimeNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_AVERAGE_PULL_DELAY_NANOS,
- (long long)pair.second.avgPullDelayNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_PULL_DELAY_NANOS,
- (long long)pair.second.maxPullDelayNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DATA_ERROR, (long long)pair.second.dataError);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT,
- (long long)pair.second.pullTimeout);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY,
- (long long)pair.second.pullExceedMaxDelay);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
- (long long)pair.second.pullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
- (long long)pair.second.emptyData);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
- (long long) pair.second.registeredCount);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
- (long long) pair.second.unregisteredCount);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT,
- (long long)pair.second.binderCallFailCount);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND,
- (long long)pair.second.pullUidProviderNotFound);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND,
- (long long)pair.second.pullerNotFound);
- for (const auto& pullTimeoutMetadata : pair.second.pullTimeoutMetadata) {
- uint64_t timeoutMetadataToken = protoOutput->start(FIELD_TYPE_MESSAGE |
- FIELD_ID_PULL_TIMEOUT_METADATA |
- FIELD_COUNT_REPEATED);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_UPTIME_MILLIS,
- pullTimeoutMetadata.pullTimeoutUptimeMillis);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_TIMEOUT_METADATA_ELAPSED_MILLIS,
- pullTimeoutMetadata.pullTimeoutElapsedMillis);
- protoOutput->end(timeoutMetadataToken);
- }
- protoOutput->end(token);
-}
-
-void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
- util::ProtoOutputStream *protoOutput) {
- uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS |
- FIELD_COUNT_REPEATED);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
- (long long)pair.second.hardDimensionLimitReached);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
- (long long)pair.second.lateLogEventSkipped);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS,
- (long long)pair.second.skippedForwardBuckets);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE,
- (long long)pair.second.badValueType);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET,
- (long long)pair.second.conditionChangeInNextBucket);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET,
- (long long)pair.second.invalidatedBucket);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED,
- (long long)pair.second.bucketDropped);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS,
- (long long)pair.second.minBucketBoundaryDelayNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
- (long long)pair.second.maxBucketBoundaryDelayNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
- (long long)pair.second.bucketUnknownCondition);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT,
- (long long)pair.second.bucketCount);
- protoOutput->end(token);
-}
-
-int64_t getElapsedRealtimeNs() {
- return ::android::elapsedRealtimeNano();
-}
-
-int64_t getElapsedRealtimeSec() {
- return ::android::elapsedRealtimeNano() / NS_PER_SEC;
-}
-
-int64_t getElapsedRealtimeMillis() {
- return ::android::elapsedRealtime();
-}
-
-int64_t getSystemUptimeMillis() {
- return ::android::uptimeMillis();
-}
-
-int64_t getWallClockNs() {
- return time(nullptr) * NS_PER_SEC;
-}
-
-int64_t getWallClockSec() {
- return time(nullptr);
-}
-
-int64_t getWallClockMillis() {
- return time(nullptr) * MS_PER_SEC;
-}
-
-int64_t truncateTimestampIfNecessary(const LogEvent& event) {
- if (event.shouldTruncateTimestamp() ||
- (event.GetTagId() >= StatsdStats::kTimestampTruncationStartTag &&
- event.GetTagId() <= StatsdStats::kTimestampTruncationEndTag)) {
- return event.GetElapsedTimestampNs() / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
- } else {
- return event.GetElapsedTimestampNs();
- }
-}
-
-int64_t NanoToMillis(const int64_t nano) {
- return nano / 1000000;
-}
-
-int64_t MillisToNano(const int64_t millis) {
- return millis * 1000000;
-}
-
-bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) {
- shared_ptr<IStatsCompanionService> scs = getStatsCompanionService();
- if (scs == nullptr) {
- return false;
- }
-
- bool success;
- ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success);
- if (!status.isOk()) {
- return false;
- }
-
- return success;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
deleted file mode 100644
index 10e065e4113f..000000000000
--- a/cmds/statsd/src/stats_log_util.h
+++ /dev/null
@@ -1,117 +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.
- */
-
-#pragma once
-
-#include <android/util/ProtoOutputStream.h>
-
-#include "FieldValue.h"
-#include "HashableDimensionKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "guardrail/StatsdStats.h"
-#include "logd/LogEvent.h"
-
-using android::util::ProtoOutputStream;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
- ProtoOutputStream* protoOutput);
-void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
- ProtoOutputStream* protoOutput);
-
-void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
- const int dimensionLeafFieldId,
- std::set<string> *str_set,
- ProtoOutputStream* protoOutput);
-
-void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
- ProtoOutputStream* protoOutput);
-
-void writeStateToProto(const FieldValue& state, ProtoOutputStream* protoOutput);
-
-// Convert the TimeUnit enum to the bucket size in millis with a guardrail on
-// bucket size.
-int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit);
-
-// Convert the TimeUnit enum to the bucket size in millis.
-int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit);
-
-// Gets the elapsed timestamp in ns.
-int64_t getElapsedRealtimeNs();
-
-// Gets the elapsed timestamp in millis.
-int64_t getElapsedRealtimeMillis();
-
-// Gets the elapsed timestamp in seconds.
-int64_t getElapsedRealtimeSec();
-
-// Gets the system uptime in millis.
-int64_t getSystemUptimeMillis();
-
-// Gets the wall clock timestamp in ns.
-int64_t getWallClockNs();
-
-// Gets the wall clock timestamp in millis.
-int64_t getWallClockMillis();
-
-// Gets the wall clock timestamp in seconds.
-int64_t getWallClockSec();
-
-int64_t NanoToMillis(const int64_t nano);
-
-int64_t MillisToNano(const int64_t millis);
-
-// Helper function to write PulledAtomStats to ProtoOutputStream
-void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
- ProtoOutputStream* protoOutput);
-
-// Helper function to write AtomMetricStats to ProtoOutputStream
-void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
- ProtoOutputStream *protoOutput);
-
-template<class T>
-bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) {
- std::string pbBytes;
- sp<android::util::ProtoReader> reader = protoOutput.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- pbBytes.append(reinterpret_cast<const char*>(reader->readBuffer()), toRead);
- reader->move(toRead);
- }
- return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
-}
-
-// Checks the truncate timestamp annotation as well as the restricted range of 300,000 - 304,999.
-// Returns the truncated timestamp to the nearest 5 minutes if needed.
-int64_t truncateTimestampIfNecessary(const LogEvent& event);
-
-// Checks permission for given pid and uid.
-bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid);
-
-inline bool isVendorPulledAtom(int atomId) {
- return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag;
-}
-
-inline bool isPulledAtom(int atomId) {
- return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
deleted file mode 100644
index cfc411fdd25f..000000000000
--- a/cmds/statsd/src/stats_util.h
+++ /dev/null
@@ -1,36 +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.
- */
-
-#pragma once
-
-#include "HashableDimensionKey.h"
-
-#include <unordered_map>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
-const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey();
-
-typedef std::map<int64_t, HashableDimensionKey> ConditionKey;
-
-typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap;
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp
deleted file mode 100644
index ce07ec0ea884..000000000000
--- a/cmds/statsd/src/statscompanion_util.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "statscompanion_util.h"
-#include <android/binder_auto_utils.h>
-#include <android/binder_manager.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-shared_ptr<IStatsCompanionService> getStatsCompanionService() {
- ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion"));
- return IStatsCompanionService::fromBinder(binder);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h
deleted file mode 100644
index e20c40bba104..000000000000
--- a/cmds/statsd/src/statscompanion_util.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/os/IStatsCompanionService.h>
-
-using aidl::android::os::IStatsCompanionService;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/** Fetches and returns the StatsCompanionService. */
-shared_ptr<IStatsCompanionService> getStatsCompanionService();
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
deleted file mode 100644
index acdffd3d4712..000000000000
--- a/cmds/statsd/src/statsd_config.proto
+++ /dev/null
@@ -1,511 +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.
- */
-
-syntax = "proto2";
-
-package android.os.statsd;
-
-option java_package = "com.android.internal.os";
-option java_outer_classname = "StatsdConfigProto";
-
-enum Position {
- POSITION_UNKNOWN = 0;
-
- FIRST = 1;
-
- LAST = 2;
-
- ANY = 3;
-
- ALL = 4;
-}
-
-enum TimeUnit {
- TIME_UNIT_UNSPECIFIED = 0;
- ONE_MINUTE = 1; // WILL BE GUARDRAILED TO 5 MINS UNLESS UID = SHELL OR ROOT
- FIVE_MINUTES = 2;
- TEN_MINUTES = 3;
- THIRTY_MINUTES = 4;
- ONE_HOUR = 5;
- THREE_HOURS = 6;
- SIX_HOURS = 7;
- TWELVE_HOURS = 8;
- ONE_DAY = 9;
- ONE_WEEK = 10;
- CTS = 1000;
-}
-
-message FieldMatcher {
- optional int32 field = 1;
-
- optional Position position = 2;
-
- repeated FieldMatcher child = 3;
-}
-
-message FieldValueMatcher {
- optional int32 field = 1;
-
- optional Position position = 2;
-
- oneof value_matcher {
- bool eq_bool = 3;
- string eq_string = 4;
- int64 eq_int = 5;
-
- int64 lt_int = 6;
- int64 gt_int = 7;
- float lt_float = 8;
- float gt_float = 9;
-
- int64 lte_int = 10;
- int64 gte_int = 11;
-
- MessageMatcher matches_tuple = 12;
-
- StringListMatcher eq_any_string = 13;
- StringListMatcher neq_any_string = 14;
- }
-}
-
-message MessageMatcher {
- repeated FieldValueMatcher field_value_matcher = 1;
-}
-
-message StringListMatcher {
- repeated string str_value = 1;
-}
-
-enum LogicalOperation {
- LOGICAL_OPERATION_UNSPECIFIED = 0;
- AND = 1;
- OR = 2;
- NOT = 3;
- NAND = 4;
- NOR = 5;
-}
-
-message SimpleAtomMatcher {
- optional int32 atom_id = 1;
-
- repeated FieldValueMatcher field_value_matcher = 2;
-}
-
-message AtomMatcher {
- optional int64 id = 1;
-
- message Combination {
- optional LogicalOperation operation = 1;
-
- repeated int64 matcher = 2;
- }
- oneof contents {
- SimpleAtomMatcher simple_atom_matcher = 2;
- Combination combination = 3;
- }
-}
-
-message SimplePredicate {
- optional int64 start = 1;
-
- optional int64 stop = 2;
-
- optional bool count_nesting = 3 [default = true];
-
- optional int64 stop_all = 4;
-
- enum InitialValue {
- UNKNOWN = 0;
- FALSE = 1;
- }
- optional InitialValue initial_value = 5 [default = UNKNOWN];
-
- optional FieldMatcher dimensions = 6;
-}
-
-message Predicate {
- optional int64 id = 1;
-
- message Combination {
- optional LogicalOperation operation = 1;
-
- repeated int64 predicate = 2;
- }
-
- oneof contents {
- SimplePredicate simple_predicate = 2;
- Combination combination = 3;
- }
-}
-
-message StateMap {
- message StateGroup {
- optional int64 group_id = 1;
-
- repeated int32 value = 2;
- }
-
- repeated StateGroup group = 1;
-}
-
-message State {
- optional int64 id = 1;
-
- optional int32 atom_id = 2;
-
- optional StateMap map = 3;
-}
-
-message MetricConditionLink {
- optional int64 condition = 1;
-
- optional FieldMatcher fields_in_what = 2;
-
- optional FieldMatcher fields_in_condition = 3;
-}
-
-message MetricStateLink {
- optional int32 state_atom_id = 1;
-
- optional FieldMatcher fields_in_what = 2;
-
- optional FieldMatcher fields_in_state = 3;
-}
-
-message FieldFilter {
- optional bool include_all = 1 [default = false];
- optional FieldMatcher fields = 2;
-}
-
-message EventMetric {
- optional int64 id = 1;
-
- optional int64 what = 2;
-
- optional int64 condition = 3;
-
- repeated MetricConditionLink links = 4;
-
- reserved 100;
- reserved 101;
-}
-
-message CountMetric {
- optional int64 id = 1;
-
- optional int64 what = 2;
-
- optional int64 condition = 3;
-
- optional FieldMatcher dimensions_in_what = 4;
-
- repeated int64 slice_by_state = 8;
-
- optional TimeUnit bucket = 5;
-
- repeated MetricConditionLink links = 6;
-
- repeated MetricStateLink state_link = 9;
-
- optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
-
- reserved 100;
- reserved 101;
-}
-
-message DurationMetric {
- optional int64 id = 1;
-
- optional int64 what = 2;
-
- optional int64 condition = 3;
-
- repeated int64 slice_by_state = 9;
-
- repeated MetricConditionLink links = 4;
-
- repeated MetricStateLink state_link = 10;
-
- enum AggregationType {
- SUM = 1;
-
- MAX_SPARSE = 2;
- }
- optional AggregationType aggregation_type = 5 [default = SUM];
-
- optional FieldMatcher dimensions_in_what = 6;
-
- optional TimeUnit bucket = 7;
-
- optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
-
- reserved 100;
- reserved 101;
-}
-
-message GaugeMetric {
- optional int64 id = 1;
-
- optional int64 what = 2;
-
- optional int64 trigger_event = 12;
-
- optional FieldFilter gauge_fields_filter = 3;
-
- optional int64 condition = 4;
-
- optional FieldMatcher dimensions_in_what = 5;
-
- optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
-
- optional TimeUnit bucket = 6;
-
- repeated MetricConditionLink links = 7;
-
- enum SamplingType {
- RANDOM_ONE_SAMPLE = 1;
- ALL_CONDITION_CHANGES = 2 [deprecated = true];
- CONDITION_CHANGE_TO_TRUE = 3;
- FIRST_N_SAMPLES = 4;
- }
- optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ;
-
- optional int64 min_bucket_size_nanos = 10;
-
- optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
-
- optional int32 max_pull_delay_sec = 13 [default = 30];
-
- optional bool split_bucket_for_app_upgrade = 14 [default = true];
-
- reserved 100;
- reserved 101;
-}
-
-message ValueMetric {
- optional int64 id = 1;
-
- optional int64 what = 2;
-
- optional FieldMatcher value_field = 3;
-
- optional int64 condition = 4;
-
- optional FieldMatcher dimensions_in_what = 5;
-
- repeated int64 slice_by_state = 18;
-
- optional TimeUnit bucket = 6;
-
- repeated MetricConditionLink links = 7;
-
- repeated MetricStateLink state_link = 19;
-
- enum AggregationType {
- SUM = 1;
- MIN = 2;
- MAX = 3;
- AVG = 4;
- }
- optional AggregationType aggregation_type = 8 [default = SUM];
-
- optional int64 min_bucket_size_nanos = 10;
-
- optional bool use_absolute_value_on_reset = 11 [default = false];
-
- optional bool use_diff = 12;
-
- optional bool use_zero_default_base = 15 [default = false];
-
- enum ValueDirection {
- UNKNOWN = 0;
- INCREASING = 1;
- DECREASING = 2;
- ANY = 3;
- }
- optional ValueDirection value_direction = 13 [default = INCREASING];
-
- optional bool skip_zero_diff_output = 14 [default = true];
-
- optional int32 max_pull_delay_sec = 16 [default = 30];
-
- optional bool split_bucket_for_app_upgrade = 17 [default = true];
-
- optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
-
- reserved 100;
- reserved 101;
-}
-
-message Alert {
- optional int64 id = 1;
-
- optional int64 metric_id = 2;
-
- optional int32 num_buckets = 3;
-
- optional int32 refractory_period_secs = 4;
-
- optional double trigger_if_sum_gt = 5;
-}
-
-message Alarm {
- optional int64 id = 1;
-
- optional int64 offset_millis = 2;
-
- optional int64 period_millis = 3;
-}
-
-message IncidentdDetails {
- repeated int32 section = 1;
-
- enum Destination {
- AUTOMATIC = 0;
- EXPLICIT = 1;
- }
- optional Destination dest = 2;
-
- // Package name of the incident report receiver.
- optional string receiver_pkg = 3;
-
- // Class name of the incident report receiver.
- optional string receiver_cls = 4;
-
- optional string alert_description = 5;
-}
-
-message PerfettoDetails {
- // The |trace_config| field is a proto-encoded message of type
- // perfetto.protos.TraceConfig defined in
- // //external/perfetto/protos/perfetto/config/. On device,
- // statsd doesn't need to deserialize the message as it's just
- // passed binary-encoded to the perfetto cmdline client.
- optional bytes trace_config = 1;
-}
-
-message BroadcastSubscriberDetails {
- optional int64 subscriber_id = 1;
- repeated string cookie = 2;
-}
-
-message Subscription {
- optional int64 id = 1;
-
- enum RuleType {
- RULE_TYPE_UNSPECIFIED = 0;
- ALARM = 1;
- ALERT = 2;
- }
- optional RuleType rule_type = 2;
-
- optional int64 rule_id = 3;
-
- oneof subscriber_information {
- IncidentdDetails incidentd_details = 4;
- PerfettoDetails perfetto_details = 5;
- BroadcastSubscriberDetails broadcast_subscriber_details = 6;
- }
-
- optional float probability_of_informing = 7 [default = 1.1];
-
- // This was used for perfprofd historically.
- reserved 8;
-}
-
-enum ActivationType {
- ACTIVATION_TYPE_UNKNOWN = 0;
- ACTIVATE_IMMEDIATELY = 1;
- ACTIVATE_ON_BOOT = 2;
-}
-
-message EventActivation {
- optional int64 atom_matcher_id = 1;
- optional int64 ttl_seconds = 2;
- optional int64 deactivation_atom_matcher_id = 3;
- optional ActivationType activation_type = 4;
-}
-
-message MetricActivation {
- optional int64 metric_id = 1;
-
- optional ActivationType activation_type = 3 [deprecated = true];
-
- repeated EventActivation event_activation = 2;
-}
-
-message PullAtomPackages {
- optional int32 atom_id = 1;
-
- repeated string packages = 2;
-}
-
-message StatsdConfig {
- optional int64 id = 1;
-
- repeated EventMetric event_metric = 2;
-
- repeated CountMetric count_metric = 3;
-
- repeated ValueMetric value_metric = 4;
-
- repeated GaugeMetric gauge_metric = 5;
-
- repeated DurationMetric duration_metric = 6;
-
- repeated AtomMatcher atom_matcher = 7;
-
- repeated Predicate predicate = 8;
-
- repeated Alert alert = 9;
-
- repeated Alarm alarm = 10;
-
- repeated Subscription subscription = 11;
-
- repeated string allowed_log_source = 12;
-
- repeated int64 no_report_metric = 13;
-
- message Annotation {
- optional int64 field_int64 = 1;
- optional int32 field_int32 = 2;
- }
- repeated Annotation annotation = 14;
-
- optional int64 ttl_in_seconds = 15;
-
- optional bool hash_strings_in_metric_report = 16 [default = true];
-
- repeated MetricActivation metric_activation = 17;
-
- optional bool version_strings_in_metric_report = 18;
-
- optional bool installer_in_metric_report = 19;
-
- optional bool persist_locally = 20 [default = false];
-
- repeated State state = 21;
-
- repeated string default_pull_packages = 22;
-
- repeated PullAtomPackages pull_atom_packages = 23;
-
- repeated int32 whitelisted_atom_ids = 24;
-
- // Field number 1000 is reserved for later use.
- reserved 1000;
-}
diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto
deleted file mode 100644
index 200b392f7542..000000000000
--- a/cmds/statsd/src/statsd_metadata.proto
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package android.os.statsd.metadata;
-
-message ConfigKey {
- optional int64 config_id = 1;
- optional int32 uid = 2;
-}
-
-message Field {
- optional int32 tag = 1;
- optional int32 field = 2;
-}
-
-message FieldValue {
- optional Field field = 1;
- oneof value {
- int32 value_int = 2;
- int64 value_long = 3;
- float value_float = 4;
- double value_double = 5;
- string value_str = 6;
- bytes value_storage = 7;
- }
-}
-
-message MetricDimensionKey {
- repeated FieldValue dimension_key_in_what = 1;
- repeated FieldValue state_values_key = 2;
-}
-
-message AlertDimensionKeyedData {
- // The earliest time the alert can be fired again in wall clock time.
- optional int32 last_refractory_ends_sec = 1;
- optional MetricDimensionKey dimension_key = 2;
-}
-
-message AlertMetadata {
- optional int64 alert_id = 1;
- repeated AlertDimensionKeyedData alert_dim_keyed_data = 2;
-}
-
-// All metadata for a config in statsd
-message StatsMetadata {
- optional ConfigKey config_key = 1;
- repeated AlertMetadata alert_metadata = 2;
-}
-
-message StatsMetadataList {
- repeated StatsMetadata stats_metadata = 1;
-} \ No newline at end of file
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
deleted file mode 100644
index dcfdfe3aae53..000000000000
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ /dev/null
@@ -1,781 +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.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "android-base/stringprintf.h"
-#include "guardrail/StatsdStats.h"
-#include "storage/StorageManager.h"
-#include "stats_log_util.h"
-
-#include <android-base/file.h>
-#include <private/android_filesystem_config.h>
-#include <fstream>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_MESSAGE;
-using std::map;
-
-/**
- * NOTE: these directories are protected by SELinux, any changes here must also update
- * the SELinux policies.
- */
-#define STATS_DATA_DIR "/data/misc/stats-data"
-#define STATS_SERVICE_DIR "/data/misc/stats-service"
-#define TRAIN_INFO_DIR "/data/misc/train-info"
-#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
-
-// Magic word at the start of the train info file, change this if changing the file format
-const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
-
-// for ConfigMetricsReportList
-const int FIELD_ID_REPORTS = 2;
-
-std::mutex StorageManager::sTrainInfoMutex;
-
-using android::base::StringPrintf;
-using std::unique_ptr;
-
-struct FileName {
- int64_t mTimestampSec;
- int mUid;
- int64_t mConfigId;
- bool mIsHistory;
- string getFullFileName(const char* path) {
- return StringPrintf("%s/%lld_%d_%lld%s", path, (long long)mTimestampSec, (int)mUid,
- (long long)mConfigId, (mIsHistory ? "_history" : ""));
- };
-};
-
-string StorageManager::getDataFileName(long wallClockSec, int uid, int64_t id) {
- return StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR, wallClockSec, uid,
- (long long)id);
-}
-
-string StorageManager::getDataHistoryFileName(long wallClockSec, int uid, int64_t id) {
- return StringPrintf("%s/%ld_%d_%lld_history", STATS_DATA_DIR, wallClockSec, uid,
- (long long)id);
-}
-
-static string findTrainInfoFileNameLocked(const string& trainName) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
- if (dir == NULL) {
- VLOG("Path %s does not exist", TRAIN_INFO_DIR);
- return "";
- }
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* fileName = de->d_name;
- if (fileName[0] == '.') continue;
-
- size_t fileNameLength = strlen(fileName);
- if (fileNameLength >= trainName.length()) {
- if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
- trainName.length())) {
- return string(fileName);
- }
- }
- }
-
- return "";
-}
-
-// Returns array of int64_t which contains timestamp in seconds, uid,
-// configID and whether the file is a local history file.
-static void parseFileName(char* name, FileName* output) {
- int64_t result[3];
- int index = 0;
- char* substr = strtok(name, "_");
- while (substr != nullptr && index < 3) {
- result[index] = StrToInt64(substr);
- index++;
- substr = strtok(nullptr, "_");
- }
- // When index ends before hitting 3, file name is corrupted. We
- // intentionally put -1 at index 0 to indicate the error to caller.
- // TODO(b/110563137): consider removing files with unexpected name format.
- if (index < 3) {
- result[0] = -1;
- }
-
- output->mTimestampSec = result[0];
- output->mUid = result[1];
- output->mConfigId = result[2];
- // check if the file is a local history.
- output->mIsHistory = (substr != nullptr && strcmp("history", substr) == 0);
-}
-
-void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) {
- int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
- if (fd == -1) {
- VLOG("Attempt to access %s but failed", file);
- return;
- }
- trimToFit(STATS_SERVICE_DIR);
- trimToFit(STATS_DATA_DIR);
-
- if (android::base::WriteFully(fd, buffer, numBytes)) {
- VLOG("Successfully wrote %s", file);
- } else {
- ALOGE("Failed to write %s", file);
- }
-
- int result = fchown(fd, AID_STATSD, AID_STATSD);
- if (result) {
- VLOG("Failed to chown %s to statsd", file);
- }
-
- close(fd);
-}
-
-bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
- std::lock_guard<std::mutex> lock(sTrainInfoMutex);
-
- if (trainInfo.trainName.empty()) {
- return false;
- }
- deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
-
- std::string fileName =
- StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
- trainInfo.trainName.c_str());
-
- int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
- if (fd == -1) {
- VLOG("Attempt to access %s but failed", fileName.c_str());
- return false;
- }
-
- size_t result;
- // Write the magic word
- result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
- if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
- VLOG("Failed to wrtie train info magic");
- close(fd);
- return false;
- }
-
- // Write the train version
- const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
- result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
- if (result != trainVersionCodeByteCount) {
- VLOG("Failed to wrtie train version code");
- close(fd);
- return false;
- }
-
- // Write # of bytes in trainName to file
- const size_t trainNameSize = trainInfo.trainName.size();
- const size_t trainNameSizeByteCount = sizeof(trainNameSize);
- result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
- if (result != trainNameSizeByteCount) {
- VLOG("Failed to write train name size");
- close(fd);
- return false;
- }
-
- // Write trainName to file
- result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
- if (result != trainNameSize) {
- VLOG("Failed to write train name");
- close(fd);
- return false;
- }
-
- // Write status to file
- const size_t statusByteCount = sizeof(trainInfo.status);
- result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
- if (result != statusByteCount) {
- VLOG("Failed to write status");
- close(fd);
- return false;
- }
-
- // Write experiment id count to file.
- const size_t experimentIdsCount = trainInfo.experimentIds.size();
- const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
- result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
- if (result != experimentIdsCountByteCount) {
- VLOG("Failed to write experiment id count");
- close(fd);
- return false;
- }
-
- // Write experimentIds to file
- for (size_t i = 0; i < experimentIdsCount; i++) {
- const int64_t experimentId = trainInfo.experimentIds[i];
- const size_t experimentIdByteCount = sizeof(experimentId);
- result = write(fd, &experimentId, experimentIdByteCount);
- if (result == experimentIdByteCount) {
- VLOG("Successfully wrote experiment IDs");
- } else {
- VLOG("Failed to write experiment ids");
- close(fd);
- return false;
- }
- }
-
- // Write bools to file
- const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
- result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
- if (result != boolByteCount) {
- VLOG("Failed to write requires staging");
- close(fd);
- return false;
- }
-
- result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
- if (result != boolByteCount) {
- VLOG("Failed to write rollback enabled");
- close(fd);
- return false;
- }
-
- result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
- if (result != boolByteCount) {
- VLOG("Failed to write requires log latency monitor");
- close(fd);
- return false;
- }
-
- close(fd);
- return true;
-}
-
-bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
- std::lock_guard<std::mutex> lock(sTrainInfoMutex);
- return readTrainInfoLocked(trainName, trainInfo);
-}
-
-bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
- trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
- string fileName = findTrainInfoFileNameLocked(trainName);
- if (fileName.empty()) {
- return false;
- }
- int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- VLOG("Failed to open %s", fileName.c_str());
- return false;
- }
-
- // Read the magic word
- uint32_t magic;
- size_t result = read(fd, &magic, sizeof(magic));
- if (result != sizeof(magic)) {
- VLOG("Failed to read train info magic");
- close(fd);
- return false;
- }
-
- if (magic != TRAIN_INFO_FILE_MAGIC) {
- VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC);
- close(fd);
- return false;
- }
-
- // Read the train version code
- const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode));
- result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
- if (result != trainVersionCodeByteCount) {
- VLOG("Failed to read train version code from train info file");
- close(fd);
- return false;
- }
-
- // Read # of bytes taken by trainName in the file.
- size_t trainNameSize;
- result = read(fd, &trainNameSize, sizeof(size_t));
- if (result != sizeof(size_t)) {
- VLOG("Failed to read train name size from train info file");
- close(fd);
- return false;
- }
-
- // Read trainName
- trainInfo.trainName.resize(trainNameSize);
- result = read(fd, trainInfo.trainName.data(), trainNameSize);
- if (result != trainNameSize) {
- VLOG("Failed to read train name from train info file");
- close(fd);
- return false;
- }
-
- // Read status
- const size_t statusByteCount = sizeof(trainInfo.status);
- result = read(fd, &trainInfo.status, statusByteCount);
- if (result != statusByteCount) {
- VLOG("Failed to read train status from train info file");
- close(fd);
- return false;
- }
-
- // Read experiment ids count.
- size_t experimentIdsCount;
- result = read(fd, &experimentIdsCount, sizeof(size_t));
- if (result != sizeof(size_t)) {
- VLOG("Failed to read train experiment id count from train info file");
- close(fd);
- return false;
- }
-
- // Read experimentIds
- for (size_t i = 0; i < experimentIdsCount; i++) {
- int64_t experimentId;
- result = read(fd, &experimentId, sizeof(experimentId));
- if (result != sizeof(experimentId)) {
- VLOG("Failed to read train experiment id from train info file");
- close(fd);
- return false;
- }
- trainInfo.experimentIds.push_back(experimentId);
- }
-
- // Read bools
- const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
- result = read(fd, &trainInfo.requiresStaging, boolByteCount);
- if (result != boolByteCount) {
- VLOG("Failed to read requires requires staging from train info file");
- close(fd);
- return false;
- }
-
- result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
- if (result != boolByteCount) {
- VLOG("Failed to read requires rollback enabled from train info file");
- close(fd);
- return false;
- }
-
- result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
- if (result != boolByteCount) {
- VLOG("Failed to read requires requires low latency monitor from train info file");
- close(fd);
- return false;
- }
-
- // Expect to be at EOF.
- char c;
- result = read(fd, &c, 1);
- if (result != 0) {
- VLOG("Failed to read train info from file. Did not get expected EOF.");
- close(fd);
- return false;
- }
-
- VLOG("Read train info file successful");
- close(fd);
- return true;
-}
-
-vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
- std::lock_guard<std::mutex> lock(sTrainInfoMutex);
- vector<InstallTrainInfo> trainInfoList;
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
- if (dir == NULL) {
- VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
- return trainInfoList;
- }
-
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') {
- continue;
- }
-
- InstallTrainInfo trainInfo;
- bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
- if (!readSuccess) {
- continue;
- }
- trainInfoList.push_back(trainInfo);
- }
- return trainInfoList;
-}
-
-void StorageManager::deleteFile(const char* file) {
- if (remove(file) != 0) {
- VLOG("Attempt to delete %s but is not found", file);
- } else {
- VLOG("Successfully deleted %s", file);
- }
-}
-
-void StorageManager::deleteAllFiles(const char* path) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
- if (dir == NULL) {
- VLOG("Directory does not exist: %s", path);
- return;
- }
-
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') continue;
- deleteFile(StringPrintf("%s/%s", path, name).c_str());
- }
-}
-
-void StorageManager::deleteSuffixedFiles(const char* path, const char* suffix) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
- if (dir == NULL) {
- VLOG("Directory does not exist: %s", path);
- return;
- }
-
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') {
- continue;
- }
- size_t nameLen = strlen(name);
- size_t suffixLen = strlen(suffix);
- if (suffixLen <= nameLen && strncmp(name + nameLen - suffixLen, suffix, suffixLen) == 0) {
- deleteFile(StringPrintf("%s/%s", path, name).c_str());
- }
- }
-}
-
-void StorageManager::sendBroadcast(const char* path,
- const std::function<void(const ConfigKey&)>& sendBroadcast) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
- if (dir == NULL) {
- VLOG("no stats-data directory on disk");
- return;
- }
-
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') continue;
- VLOG("file %s", name);
-
- FileName output;
- parseFileName(name, &output);
- if (output.mTimestampSec == -1 || output.mIsHistory) continue;
- sendBroadcast(ConfigKey((int)output.mUid, output.mConfigId));
- }
-}
-
-bool StorageManager::hasConfigMetricsReport(const ConfigKey& key) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
- if (dir == NULL) {
- VLOG("Path %s does not exist", STATS_DATA_DIR);
- return false;
- }
-
- string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
-
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') continue;
-
- size_t nameLen = strlen(name);
- size_t suffixLen = suffix.length();
- if (suffixLen <= nameLen &&
- strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
- // Check again that the file name is parseable.
- FileName output;
- parseFileName(name, &output);
- if (output.mTimestampSec == -1 || output.mIsHistory) continue;
- return true;
- }
- }
- return false;
-}
-
-void StorageManager::appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto,
- bool erase_data, bool isAdb) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_DATA_DIR), closedir);
- if (dir == NULL) {
- VLOG("Path %s does not exist", STATS_DATA_DIR);
- return;
- }
-
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- string fileName(name);
- if (name[0] == '.') continue;
- FileName output;
- parseFileName(name, &output);
-
- if (output.mTimestampSec == -1 || (output.mIsHistory && !isAdb) ||
- output.mUid != key.GetUid() || output.mConfigId != key.GetId()) {
- continue;
- }
-
- auto fullPathName = StringPrintf("%s/%s", STATS_DATA_DIR, fileName.c_str());
- int fd = open(fullPathName.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd != -1) {
- string content;
- if (android::base::ReadFdToString(fd, &content)) {
- proto->write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS,
- content.c_str(), content.size());
- }
- close(fd);
- } else {
- ALOGE("file cannot be opened");
- }
-
- if (erase_data) {
- remove(fullPathName.c_str());
- } else if (!output.mIsHistory && !isAdb) {
- // This means a real data owner has called to get this data. But the config says it
- // wants to keep a local history. So now this file must be renamed as a history file.
- // So that next time, when owner calls getData() again, this data won't be uploaded
- // again. rename returns 0 on success
- if (rename(fullPathName.c_str(), (fullPathName + "_history").c_str())) {
- ALOGE("Failed to rename file %s", fullPathName.c_str());
- }
- }
- }
-}
-
-bool StorageManager::readFileToString(const char* file, string* content) {
- int fd = open(file, O_RDONLY | O_CLOEXEC);
- bool res = false;
- if (fd != -1) {
- if (android::base::ReadFdToString(fd, content)) {
- res = true;
- } else {
- VLOG("Failed to read file %s\n", file);
- }
- close(fd);
- }
- return res;
-}
-
-void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
- if (dir == NULL) {
- VLOG("no default config on disk");
- return;
- }
- trimToFit(STATS_SERVICE_DIR);
-
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') continue;
-
- FileName output;
- parseFileName(name, &output);
- if (output.mTimestampSec == -1) continue;
- string file_name = output.getFullFileName(STATS_SERVICE_DIR);
- int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
- if (fd != -1) {
- string content;
- if (android::base::ReadFdToString(fd, &content)) {
- StatsdConfig config;
- if (config.ParseFromString(content)) {
- configsMap[ConfigKey(output.mUid, output.mConfigId)] = config;
- VLOG("map key uid=%lld|configID=%lld", (long long)output.mUid,
- (long long)output.mConfigId);
- }
- }
- close(fd);
- }
- }
-}
-
-bool StorageManager::readConfigFromDisk(const ConfigKey& key, StatsdConfig* config) {
- string content;
- return config != nullptr &&
- StorageManager::readConfigFromDisk(key, &content) && config->ParseFromString(content);
-}
-
-bool StorageManager::readConfigFromDisk(const ConfigKey& key, string* content) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR),
- closedir);
- if (dir == NULL) {
- VLOG("Directory does not exist: %s", STATS_SERVICE_DIR);
- return false;
- }
-
- string suffix = StringPrintf("%d_%lld", key.GetUid(), (long long)key.GetId());
- dirent* de;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') {
- continue;
- }
- size_t nameLen = strlen(name);
- size_t suffixLen = suffix.length();
- // There can be at most one file that matches this suffix (config key).
- if (suffixLen <= nameLen &&
- strncmp(name + nameLen - suffixLen, suffix.c_str(), suffixLen) == 0) {
- int fd = open(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str(),
- O_RDONLY | O_CLOEXEC);
- if (fd != -1) {
- if (android::base::ReadFdToString(fd, content)) {
- return true;
- }
- close(fd);
- }
- }
- }
- return false;
-}
-
-bool StorageManager::hasIdenticalConfig(const ConfigKey& key,
- const vector<uint8_t>& config) {
- string content;
- if (StorageManager::readConfigFromDisk(key, &content)) {
- vector<uint8_t> vec(content.begin(), content.end());
- if (vec == config) {
- return true;
- }
- }
- return false;
-}
-
-void StorageManager::sortFiles(vector<FileInfo>* fileNames) {
- // Reverse sort to effectively remove from the back (oldest entries).
- // This will sort files in reverse-chronological order. Local history files have lower
- // priority than regular data files.
- sort(fileNames->begin(), fileNames->end(), [](FileInfo& lhs, FileInfo& rhs) {
- // first consider if the file is a local history
- if (lhs.mIsHistory && !rhs.mIsHistory) {
- return false;
- } else if (rhs.mIsHistory && !lhs.mIsHistory) {
- return true;
- }
-
- // then consider the age.
- if (lhs.mFileAgeSec < rhs.mFileAgeSec) {
- return true;
- } else if (lhs.mFileAgeSec > rhs.mFileAgeSec) {
- return false;
- }
-
- // then good luck.... use string::compare
- return lhs.mFileName.compare(rhs.mFileName) > 0;
- });
-}
-
-void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
- if (dir == NULL) {
- VLOG("Path %s does not exist", path);
- return;
- }
- dirent* de;
- int totalFileSize = 0;
- vector<FileInfo> fileNames;
- auto nowSec = getWallClockSec();
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') continue;
-
- FileName output;
- string file_name;
- if (parseTimestampOnly) {
- file_name = StringPrintf("%s/%s", path, name);
- output.mTimestampSec = StrToInt64(strtok(name, "_"));
- output.mIsHistory = false;
- } else {
- parseFileName(name, &output);
- file_name = output.getFullFileName(path);
- }
- if (output.mTimestampSec == -1) continue;
-
- // Check for timestamp and delete if it's too old.
- long fileAge = nowSec - output.mTimestampSec;
- if (fileAge > StatsdStats::kMaxAgeSecond ||
- (output.mIsHistory && fileAge > StatsdStats::kMaxLocalHistoryAgeSecond)) {
- deleteFile(file_name.c_str());
- continue;
- }
-
- ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
- int fileSize = 0;
- if (file.is_open()) {
- file.seekg(0, ios::end);
- fileSize = file.tellg();
- file.close();
- totalFileSize += fileSize;
- }
- fileNames.emplace_back(file_name, output.mIsHistory, fileSize, fileAge);
- }
-
- if (fileNames.size() > StatsdStats::kMaxFileNumber ||
- totalFileSize > StatsdStats::kMaxFileSize) {
- sortFiles(&fileNames);
- }
-
- // Start removing files from oldest to be under the limit.
- while (fileNames.size() > 0 && (fileNames.size() > StatsdStats::kMaxFileNumber ||
- totalFileSize > StatsdStats::kMaxFileSize)) {
- totalFileSize -= fileNames.at(fileNames.size() - 1).mFileSizeBytes;
- deleteFile(fileNames.at(fileNames.size() - 1).mFileName.c_str());
- fileNames.pop_back();
- }
-}
-
-void StorageManager::printStats(int outFd) {
- printDirStats(outFd, STATS_SERVICE_DIR);
- printDirStats(outFd, STATS_DATA_DIR);
-}
-
-void StorageManager::printDirStats(int outFd, const char* path) {
- dprintf(outFd, "Printing stats of %s\n", path);
- unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
- if (dir == NULL) {
- VLOG("Path %s does not exist", path);
- return;
- }
- dirent* de;
- int fileCount = 0;
- int totalFileSize = 0;
- while ((de = readdir(dir.get()))) {
- char* name = de->d_name;
- if (name[0] == '.') {
- continue;
- }
- FileName output;
- parseFileName(name, &output);
- if (output.mTimestampSec == -1) continue;
- dprintf(outFd, "\t #%d, Last updated: %lld, UID: %d, Config ID: %lld, %s", fileCount + 1,
- (long long)output.mTimestampSec, output.mUid, (long long)output.mConfigId,
- (output.mIsHistory ? "local history" : ""));
- string file_name = output.getFullFileName(path);
- ifstream file(file_name.c_str(), ifstream::in | ifstream::binary);
- if (file.is_open()) {
- file.seekg(0, ios::end);
- int fileSize = file.tellg();
- file.close();
- dprintf(outFd, ", File Size: %d bytes", fileSize);
- totalFileSize += fileSize;
- }
- dprintf(outFd, "\n");
- fileCount++;
- }
- dprintf(outFd, "\tTotal number of files: %d, Total size of files: %d bytes.\n", fileCount,
- totalFileSize);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
deleted file mode 100644
index d59046dfbb99..000000000000
--- a/cmds/statsd/src/storage/StorageManager.h
+++ /dev/null
@@ -1,168 +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.
- */
-
-#ifndef STORAGE_MANAGER_H
-#define STORAGE_MANAGER_H
-
-#include <android/util/ProtoOutputStream.h>
-#include <utils/Log.h>
-#include <utils/RefBase.h>
-
-#include "packages/UidMap.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoOutputStream;
-
-class StorageManager : public virtual RefBase {
-public:
- struct FileInfo {
- FileInfo(std::string name, bool isHistory, int fileSize, long fileAge)
- : mFileName(name),
- mIsHistory(isHistory),
- mFileSizeBytes(fileSize),
- mFileAgeSec(fileAge) {
- }
- std::string mFileName;
- bool mIsHistory;
- int mFileSizeBytes;
- long mFileAgeSec;
- };
-
- /**
- * Writes a given byte array as a file to the specified file path.
- */
- static void writeFile(const char* file, const void* buffer, int numBytes);
-
- /**
- * Writes train info.
- */
- static bool writeTrainInfo(const InstallTrainInfo& trainInfo);
-
- /**
- * Reads train info.
- */
- static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo);
-
- /**
- * Reads train info assuming lock is obtained.
- */
- static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo);
-
- /**
- * Reads all train info and returns a vector of train info.
- */
- static vector<InstallTrainInfo> readAllTrainInfo();
-
- /**
- * Reads the file content to the buffer.
- */
- static bool readFileToString(const char* file, string* content);
-
- /**
- * Deletes a single file given a file name.
- */
- static void deleteFile(const char* file);
-
- /**
- * Deletes all files in a given directory.
- */
- static void deleteAllFiles(const char* path);
-
- /**
- * Deletes all files whose name matches with a provided suffix.
- */
- static void deleteSuffixedFiles(const char* path, const char* suffix);
-
- /**
- * Send broadcasts to relevant receiver for each data stored on disk.
- */
- static void sendBroadcast(const char* path,
- const std::function<void(const ConfigKey&)>& sendBroadcast);
-
- /**
- * Returns true if there's at least one report on disk.
- */
- static bool hasConfigMetricsReport(const ConfigKey& key);
-
- /**
- * Appends the ConfigMetricsReport found on disk to the specifid proto
- * and, if erase_data, deletes it from disk.
- *
- * [isAdb]: if the caller is adb dump. This includes local adb dump or dumpsys by
- * bugreport or incidentd. When true, we will append any local history data too.
- *
- * When
- * erase_data=true, isAdb=true: append history data to output, remove all data after read
- * erase_data=false, isAdb=true: append history data to output, keep data after read
- * erase_data=true, isAdb=false: do not append history data, and remove data after read
- * erase_data=false, isAdb=false: do not append history data and *rename* all data files to
- * history files.
- */
- static void appendConfigMetricsReport(const ConfigKey& key, ProtoOutputStream* proto,
- bool erase_data, bool isAdb);
-
- /**
- * Call to load the saved configs from disk.
- */
- static void readConfigFromDisk(std::map<ConfigKey, StatsdConfig>& configsMap);
-
- /**
- * Call to load the specified config from disk. Returns false if the config file does not
- * exist or error occurs when reading the file.
- */
- static bool readConfigFromDisk(const ConfigKey& key, StatsdConfig* config);
- static bool readConfigFromDisk(const ConfigKey& key, string* config);
-
- /**
- * Trims files in the provided directory to limit the total size, number of
- * files, accumulation of outdated files.
- */
- static void trimToFit(const char* dir, bool parseTimestampOnly = false);
-
- /**
- * Returns true if there already exists identical configuration on device.
- */
- static bool hasIdenticalConfig(const ConfigKey& key,
- const vector<uint8_t>& config);
-
- /**
- * Prints disk usage statistics related to statsd.
- */
- static void printStats(int out);
-
- static string getDataFileName(long wallClockSec, int uid, int64_t id);
-
- static string getDataHistoryFileName(long wallClockSec, int uid, int64_t id);
-
- static void sortFiles(vector<FileInfo>* fileNames);
-
-private:
- /**
- * Prints disk usage statistics about a directory related to statsd.
- */
- static void printDirStats(int out, const char* path);
-
- static std::mutex sTrainInfoMutex;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // STORAGE_MANAGER_H
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
deleted file mode 100644
index 1d77513d9d33..000000000000
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define DEBUG false
-#include "Log.h"
-
-#include "FieldValue.h"
-#include "IncidentdReporter.h"
-#include "packages/UidMap.h"
-#include "stats_log_util.h"
-
-#include <android/util/ProtoOutputStream.h>
-#include <incident/incident_report.h>
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoOutputStream;
-using std::vector;
-
-using util::FIELD_TYPE_INT32;
-using util::FIELD_TYPE_INT64;
-using util::FIELD_TYPE_MESSAGE;
-using util::FIELD_TYPE_STRING;
-
-// field ids in IncidentHeaderProto
-const int FIELD_ID_ALERT_ID = 1;
-const int FIELD_ID_REASON = 2;
-const int FIELD_ID_CONFIG_KEY = 3;
-const int FIELD_ID_CONFIG_KEY_UID = 1;
-const int FIELD_ID_CONFIG_KEY_ID = 2;
-
-const int FIELD_ID_TRIGGER_DETAILS = 4;
-const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
-const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
-const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
-const int FIELD_ID_METRIC_VALUE_VALUE = 4;
-
-const int FIELD_ID_PACKAGE_INFO = 3;
-
-namespace {
-void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
- int64_t metricValue, const ConfigKey& configKey, const string& reason,
- vector<uint8_t>* protoData) {
- ProtoOutputStream headerProto;
- headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
- headerProto.write(FIELD_TYPE_STRING | FIELD_ID_REASON, reason);
- uint64_t token =
- headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
- headerProto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_KEY_UID, configKey.GetUid());
- headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId());
- headerProto.end(token);
-
- token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS);
-
- // MetricValue trigger_metric = 1;
- uint64_t metricToken =
- headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC);
- // message MetricValue {
- // optional int64 metric_id = 1;
- headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId);
- // optional DimensionsValue dimension_in_what = 2;
- uint64_t dimToken =
- headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT);
- writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
- headerProto.end(dimToken);
-
- // deprecated field
- // optional DimensionsValue dimension_in_condition = 3;
-
- // optional int64 value = 4;
- headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
-
- // }
- headerProto.end(metricToken);
-
- // write relevant uid package info
- std::set<int32_t> uids;
-
- for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) {
- int uid = getUidIfExists(dim);
- // any uid <= 2000 are predefined AID_*
- if (uid > 2000) {
- uids.insert(uid);
- }
- }
-
- if (!uids.empty()) {
- uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
- UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
- nullptr /*string set*/, &headerProto);
- headerProto.end(token);
- }
-
- headerProto.end(token);
-
- protoData->resize(headerProto.size());
- size_t pos = 0;
- sp<android::util::ProtoReader> reader = headerProto.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((*protoData)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-}
-} // namespace
-
-bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
- const MetricDimensionKey& dimensionKey, int64_t metricValue,
- const ConfigKey& configKey) {
- if (config.section_size() == 0) {
- VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
- configKey.GetUid(), (long long)configKey.GetId());
- return false;
- }
-
- AIncidentReportArgs* args = AIncidentReportArgs_init();
-
- vector<uint8_t> protoData;
- getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey,
- config.alert_description(), &protoData);
- AIncidentReportArgs_addHeader(args, protoData.data(), protoData.size());
-
- for (int i = 0; i < config.section_size(); i++) {
- AIncidentReportArgs_addSection(args, config.section(i));
- }
-
- uint8_t dest;
- switch (config.dest()) {
- case IncidentdDetails_Destination_AUTOMATIC:
- dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
- break;
- case IncidentdDetails_Destination_EXPLICIT:
- dest = INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT;
- break;
- default:
- dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
- }
- AIncidentReportArgs_setPrivacyPolicy(args, dest);
-
- AIncidentReportArgs_setReceiverPackage(args, config.receiver_pkg().c_str());
-
- AIncidentReportArgs_setReceiverClass(args, config.receiver_cls().c_str());
-
- int err = AIncidentReportArgs_takeReport(args);
- AIncidentReportArgs_delete(args);
-
- return err == NO_ERROR;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
deleted file mode 100644
index e78a4d98dcd8..000000000000
--- a/cmds/statsd/src/subscriber/IncidentdReporter.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "HashableDimensionKey.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert, IncidentdDetails
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Calls incidentd to trigger an incident report and put in dropbox for uploading.
- */
-bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
- const MetricDimensionKey& dimensionKey, int64_t metricValue,
- const ConfigKey& configKey);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
deleted file mode 100644
index c915ef3bf069..000000000000
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "SubscriberReporter.h"
-
-using std::lock_guard;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-struct BroadcastSubscriberDeathCookie {
- BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId,
- const shared_ptr<IPendingIntentRef>& pir):
- mConfigKey(configKey),
- mSubscriberId(subscriberId),
- mPir(pir) {}
-
- ConfigKey mConfigKey;
- int64_t mSubscriberId;
- shared_ptr<IPendingIntentRef> mPir;
-};
-
-void SubscriberReporter::broadcastSubscriberDied(void* cookie) {
- auto cookie_ = static_cast<BroadcastSubscriberDeathCookie*>(cookie);
- ConfigKey& configKey = cookie_->mConfigKey;
- int64_t subscriberId = cookie_->mSubscriberId;
- shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
-
- SubscriberReporter& thiz = getInstance();
-
- // Erase the mapping from a (config_key, subscriberId) to a pir if the
- // mapping exists.
- lock_guard<mutex> lock(thiz.mLock);
- auto subscriberMapIt = thiz.mIntentMap.find(configKey);
- if (subscriberMapIt != thiz.mIntentMap.end()) {
- auto subscriberMap = subscriberMapIt->second;
- auto pirIt = subscriberMap.find(subscriberId);
- if (pirIt != subscriberMap.end() && pirIt->second == pir) {
- subscriberMap.erase(subscriberId);
- if (subscriberMap.empty()) {
- thiz.mIntentMap.erase(configKey);
- }
- }
- }
-
- // The death recipient corresponding to this specific pir can never be
- // triggered again, so free up resources.
- delete cookie_;
-}
-
-SubscriberReporter::SubscriberReporter() :
- mBroadcastSubscriberDeathRecipient(AIBinder_DeathRecipient_new(broadcastSubscriberDied)) {
-}
-
-void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
- int64_t subscriberId,
- const shared_ptr<IPendingIntentRef>& pir) {
- VLOG("SubscriberReporter::setBroadcastSubscriber called.");
- {
- lock_guard<mutex> lock(mLock);
- mIntentMap[configKey][subscriberId] = pir;
- }
- AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(),
- new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir));
-}
-
-void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
- int64_t subscriberId) {
- VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
- lock_guard<mutex> lock(mLock);
- auto subscriberMapIt = mIntentMap.find(configKey);
- if (subscriberMapIt != mIntentMap.end()) {
- subscriberMapIt->second.erase(subscriberId);
- if (subscriberMapIt->second.empty()) {
- mIntentMap.erase(configKey);
- }
- }
-}
-
-void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
- const Subscription& subscription,
- const MetricDimensionKey& dimKey) const {
- // Reminder about ids:
- // subscription id - name of the Subscription (that ties the Alert to the broadcast)
- // subscription rule_id - the name of the Alert (that triggers the broadcast)
- // subscriber_id - name of the PendingIntent to use to send the broadcast
- // config uid - the uid that uploaded the config (and therefore gave the PendingIntent,
- // although the intent may be to broadcast to a different uid)
- // config id - the name of this config (for this particular uid)
-
- VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
- lock_guard<mutex> lock(mLock);
-
- if (!subscription.has_broadcast_subscriber_details()
- || !subscription.broadcast_subscriber_details().has_subscriber_id()) {
- ALOGE("Broadcast subscriber does not have an id.");
- return;
- }
- int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
-
- vector<string> cookies;
- cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
- for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
- cookies.push_back(cookie);
- }
-
- auto it1 = mIntentMap.find(configKey);
- if (it1 == mIntentMap.end()) {
- ALOGW("Cannot inform subscriber for missing config key %s ", configKey.ToString().c_str());
- return;
- }
- auto it2 = it1->second.find(subscriberId);
- if (it2 == it1->second.end()) {
- ALOGW("Cannot inform subscriber of config %s for missing subscriberId %lld ",
- configKey.ToString().c_str(), (long long)subscriberId);
- return;
- }
- sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
-}
-
-void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
- const ConfigKey& configKey,
- const Subscription& subscription,
- const vector<string>& cookies,
- const MetricDimensionKey& dimKey) const {
- VLOG("SubscriberReporter::sendBroadcastLocked called.");
- pir->sendSubscriberBroadcast(
- configKey.GetUid(),
- configKey.GetId(),
- subscription.id(),
- subscription.rule_id(),
- cookies,
- dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
-}
-
-shared_ptr<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
- int64_t subscriberId) {
- lock_guard<mutex> lock(mLock);
- auto subscriberMapIt = mIntentMap.find(configKey);
- if (subscriberMapIt == mIntentMap.end()) {
- return nullptr;
- }
- auto pirMapIt = subscriberMapIt->second.find(subscriberId);
- if (pirMapIt == subscriberMapIt->second.end()) {
- return nullptr;
- }
- return pirMapIt->second;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
deleted file mode 100644
index 4fe428198e71..000000000000
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <aidl/android/os/IPendingIntentRef.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription
-#include "HashableDimensionKey.h"
-
-#include <mutex>
-#include <unordered_map>
-#include <vector>
-
-using aidl::android::os::IPendingIntentRef;
-using std::mutex;
-using std::shared_ptr;
-using std::string;
-using std::unordered_map;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Reports information to subscribers.
-// Single instance shared across the process. All methods are thread safe.
-class SubscriberReporter {
-public:
- /** Get (singleton) instance of SubscriberReporter. */
- static SubscriberReporter& getInstance() {
- static SubscriberReporter subscriberReporter;
- return subscriberReporter;
- }
-
- ~SubscriberReporter(){};
- SubscriberReporter(SubscriberReporter const&) = delete;
- void operator=(SubscriberReporter const&) = delete;
-
- /**
- * Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
- */
- void setBroadcastSubscriber(const ConfigKey& configKey,
- int64_t subscriberId,
- const shared_ptr<IPendingIntentRef>& pir);
-
- /**
- * Erases any intentSender information from the given (configKey, subscriberId) pair.
- */
- void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
-
- /**
- * Sends a broadcast via the intentSender previously stored for the
- * given (configKey, subscriberId) pair by setBroadcastSubscriber.
- * Information about the subscriber, as well as information extracted from the dimKey, is sent.
- */
- void alertBroadcastSubscriber(const ConfigKey& configKey,
- const Subscription& subscription,
- const MetricDimensionKey& dimKey) const;
-
- shared_ptr<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey,
- int64_t subscriberId);
-
-private:
- SubscriberReporter();
-
- mutable mutex mLock;
-
- /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
- unordered_map<ConfigKey, unordered_map<int64_t, shared_ptr<IPendingIntentRef>>> mIntentMap;
-
- /**
- * Sends a broadcast via the given intentSender (using mStatsCompanionService), along
- * with the information in the other parameters.
- */
- void sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
- const ConfigKey& configKey,
- const Subscription& subscription,
- const vector<string>& cookies,
- const MetricDimensionKey& dimKey) const;
-
- ::ndk::ScopedAIBinder_DeathRecipient mBroadcastSubscriberDeathRecipient;
-
- /**
- * Death recipient callback that is called when a broadcast subscriber dies.
- * The cookie is a pointer to a BroadcastSubscriberDeathCookie.
- */
- static void broadcastSubscriberDied(void* cookie);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/uid_data.proto b/cmds/statsd/src/uid_data.proto
deleted file mode 100644
index a6fa26cd412e..000000000000
--- a/cmds/statsd/src/uid_data.proto
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto2";
-
-package android.os.statsd;
-
-option java_package = "com.android.internal.os";
-option java_outer_classname = "UidDataProto";
-
-message ApplicationInfo {
- optional int32 uid = 1;
- optional int64 version = 2;
- optional string version_string = 3;
- optional string package_name = 4;
- optional string installer = 5;
-}
-
-// StatsServiceCompanion uses the proto to supply statsd with uid-package
-// mapping updates.
-message UidData {
- repeated ApplicationInfo app_info = 1;
-}
diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.cpp b/cmds/statsd/src/utils/MultiConditionTrigger.cpp
deleted file mode 100644
index 43a69337f368..000000000000
--- a/cmds/statsd/src/utils/MultiConditionTrigger.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define DEBUG false // STOPSHIP if true
-
-#include "MultiConditionTrigger.h"
-
-#include <thread>
-
-using namespace std;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames,
- function<void()> trigger)
- : mRemainingConditionNames(conditionNames),
- mTrigger(trigger),
- mCompleted(mRemainingConditionNames.empty()) {
- if (mCompleted) {
- thread executorThread([this] { mTrigger(); });
- executorThread.detach();
- }
-}
-
-void MultiConditionTrigger::markComplete(const string& conditionName) {
- bool doTrigger = false;
- {
- lock_guard<mutex> lg(mMutex);
- if (mCompleted) {
- return;
- }
- mRemainingConditionNames.erase(conditionName);
- mCompleted = mRemainingConditionNames.empty();
- doTrigger = mCompleted;
- }
- if (doTrigger) {
- std::thread executorThread([this] { mTrigger(); });
- executorThread.detach();
- }
-}
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.h b/cmds/statsd/src/utils/MultiConditionTrigger.h
deleted file mode 100644
index 51f6029915be..000000000000
--- a/cmds/statsd/src/utils/MultiConditionTrigger.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-#pragma once
-
-#include <gtest/gtest_prod.h>
-
-#include <mutex>
-#include <set>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * This class provides a utility to wait for a set of named conditions to occur.
- *
- * It will execute the trigger runnable in a detached thread once all conditions have been marked
- * true.
- */
-class MultiConditionTrigger {
-public:
- explicit MultiConditionTrigger(const std::set<std::string>& conditionNames,
- std::function<void()> trigger);
-
- MultiConditionTrigger(const MultiConditionTrigger&) = delete;
- MultiConditionTrigger& operator=(const MultiConditionTrigger&) = delete;
-
- // Mark a specific condition as true. If this condition has called markComplete already or if
- // the event was not specified in the constructor, the function is a no-op.
- void markComplete(const std::string& eventName);
-
-private:
- mutable std::mutex mMutex;
- std::set<std::string> mRemainingConditionNames;
- std::function<void()> mTrigger;
- bool mCompleted;
-
- FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName);
-};
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/statsd_test.xml b/cmds/statsd/statsd_test.xml
deleted file mode 100644
index 8f9bb1cb6b2a..000000000000
--- a/cmds/statsd/statsd_test.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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="Runs statsd_test.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
- <option name="test-suite-tag" value="mts" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="statsd_test->/data/local/tmp/statsd_test" />
- <option name="append-bitness" value="true" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="statsd_test" />
- </test>
-
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
- </object>
-</configuration>
diff --git a/cmds/statsd/tests/AlarmMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp
deleted file mode 100644
index 1dc9795dcf16..000000000000
--- a/cmds/statsd/tests/AlarmMonitor_test.cpp
+++ /dev/null
@@ -1,69 +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.
-
-#include "anomaly/AlarmMonitor.h"
-
-#include <gtest/gtest.h>
-
-using namespace android::os::statsd;
-using std::shared_ptr;
-
-#ifdef __ANDROID__
-TEST(AlarmMonitor, popSoonerThan) {
- std::string emptyMetricId;
- std::string emptyDimensionId;
- unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set;
- AlarmMonitor am(2,
- [](const shared_ptr<IStatsCompanionService>&, int64_t){},
- [](const shared_ptr<IStatsCompanionService>&){});
-
- set = am.popSoonerThan(5);
- EXPECT_TRUE(set.empty());
-
- sp<const InternalAlarm> a = new InternalAlarm{10};
- sp<const InternalAlarm> b = new InternalAlarm{20};
- sp<const InternalAlarm> c = new InternalAlarm{20};
- sp<const InternalAlarm> d = new InternalAlarm{30};
- sp<const InternalAlarm> e = new InternalAlarm{40};
- sp<const InternalAlarm> f = new InternalAlarm{50};
-
- am.add(a);
- am.add(b);
- am.add(c);
- am.add(d);
- am.add(e);
- am.add(f);
-
- set = am.popSoonerThan(5);
- EXPECT_TRUE(set.empty());
-
- set = am.popSoonerThan(30);
- ASSERT_EQ(4u, set.size());
- EXPECT_EQ(1u, set.count(a));
- EXPECT_EQ(1u, set.count(b));
- EXPECT_EQ(1u, set.count(c));
- EXPECT_EQ(1u, set.count(d));
-
- set = am.popSoonerThan(60);
- ASSERT_EQ(2u, set.size());
- EXPECT_EQ(1u, set.count(e));
- EXPECT_EQ(1u, set.count(f));
-
- set = am.popSoonerThan(80);
- ASSERT_EQ(0u, set.size());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
deleted file mode 100644
index 1d8371638e90..000000000000
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ /dev/null
@@ -1,159 +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.
-
-#include "src/config/ConfigManager.h"
-#include "src/metrics/MetricsManager.h"
-#include "statsd_test_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <stdio.h>
-#include <iostream>
-
-using namespace android;
-using namespace android::os::statsd;
-using namespace testing;
-using namespace std;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static ostream& operator<<(ostream& os, const StatsdConfig& config) {
- return os << "StatsdConfig{id=" << config.id() << "}";
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-/**
- * Mock ConfigListener
- */
-class MockListener : public ConfigListener {
-public:
- MOCK_METHOD4(OnConfigUpdated, void(const int64_t timestampNs, const ConfigKey& key,
- const StatsdConfig& config, bool modularUpdate));
- MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key));
-};
-
-/**
- * Validate that the ConfigKey is the one we wanted.
- */
-MATCHER_P2(ConfigKeyEq, uid, id, "") {
- return arg.GetUid() == uid && (long long)arg.GetId() == (long long)id;
-}
-
-/**
- * Validate that the StatsdConfig is the one we wanted.
- */
-MATCHER_P(StatsdConfigEq, id, 0) {
- return (long long)arg.id() == (long long)id;
-}
-
-const int64_t testConfigId = 12345;
-
-/**
- * Test the addOrUpdate and remove methods
- */
-TEST(ConfigManagerTest, TestAddUpdateRemove) {
- sp<MockListener> listener = new StrictMock<MockListener>();
-
- sp<ConfigManager> manager = new ConfigManager();
- manager->AddListener(listener);
-
- StatsdConfig config91;
- config91.set_id(91);
- StatsdConfig config92;
- config92.set_id(92);
- StatsdConfig config93;
- config93.set_id(93);
- StatsdConfig config94;
- config94.set_id(94);
-
- {
- InSequence s;
-
- manager->StartupForTest();
-
- // Add another one
- EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(91), _))
- .RetiresOnSaturation();
- manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91);
-
- // Update It
- EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(92), _))
- .RetiresOnSaturation();
- manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92);
-
- // Add one with the same uid but a different name
- EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")), StatsdConfigEq(93), _))
- .RetiresOnSaturation();
- manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93);
-
- // Add one with the same name but a different uid
- EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")), StatsdConfigEq(94), _))
- .RetiresOnSaturation();
- manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94);
-
- // Remove (1,yyy)
- EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("yyy"))))
- .RetiresOnSaturation();
- manager->RemoveConfig(ConfigKey(1, StringToId("yyy")));
-
- // Remove (2,zzz)
- EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz"))))
- .RetiresOnSaturation();
- manager->RemoveConfig(ConfigKey(2, StringToId("zzz")));
-
- // Remove (1,zzz)
- EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(1, StringToId("zzz"))))
- .RetiresOnSaturation();
- manager->RemoveConfig(ConfigKey(1, StringToId("zzz")));
-
- // Remove (2,zzz) again and we shouldn't get the callback
- manager->RemoveConfig(ConfigKey(2, StringToId("zzz")));
- }
-}
-
-/**
- * Test removing all of the configs for a uid.
- */
-TEST(ConfigManagerTest, TestRemoveUid) {
- sp<MockListener> listener = new StrictMock<MockListener>();
-
- sp<ConfigManager> manager = new ConfigManager();
- manager->AddListener(listener);
-
- StatsdConfig config;
-
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _, _, _)).Times(5);
- EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("xxx"))));
- EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("yyy"))));
- EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, StringToId("zzz"))));
-
- manager->StartupForTest();
- manager->UpdateConfig(ConfigKey(1, StringToId("aaa")), config);
- manager->UpdateConfig(ConfigKey(2, StringToId("xxx")), config);
- manager->UpdateConfig(ConfigKey(2, StringToId("yyy")), config);
- manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config);
- manager->UpdateConfig(ConfigKey(3, StringToId("bbb")), config);
-
- manager->RemoveConfigs(2);
-}
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
deleted file mode 100644
index a21eb9b9147f..000000000000
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ /dev/null
@@ -1,659 +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.
- */
-#include <gtest/gtest.h>
-
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "matchers/matcher_util.h"
-#include "src/logd/LogEvent.h"
-#include "stats_event.h"
-#include "stats_log_util.h"
-#include "stats_util.h"
-#include "subscriber/SubscriberReporter.h"
-#include "tests/statsd_test_util.h"
-
-#ifdef __ANDROID__
-
-using android::util::ProtoReader;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// These constants must be kept in sync with those in StatsDimensionsValue.java.
-const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
-const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
-const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
-const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
-
-namespace {
-void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const vector<int>& attributionUids, const vector<string>& attributionTags,
- const string& name) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, name.c_str());
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const vector<int>& attributionUids, const vector<string>& attributionTags,
- const int32_t value) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeInt32(statsEvent, value);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-} // anonymous namespace
-
-TEST(AtomMatcherTest, TestFieldTranslation) {
- FieldMatcher matcher1;
- matcher1.set_field(10);
- FieldMatcher* child = matcher1.add_child();
- child->set_field(1);
- child->set_position(Position::ANY);
-
- child = child->add_child();
- child->set_field(1);
-
- vector<Matcher> output;
- translateFieldMatcher(matcher1, &output);
-
- ASSERT_EQ((size_t)1, output.size());
-
- const auto& matcher12 = output[0];
- EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag());
- EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField());
- EXPECT_EQ((int32_t)0xff7f007f, matcher12.mMask);
-}
-
-TEST(AtomMatcherTest, TestFieldTranslation_ALL) {
- FieldMatcher matcher1;
- matcher1.set_field(10);
- FieldMatcher* child = matcher1.add_child();
- child->set_field(1);
- child->set_position(Position::ALL);
-
- child = child->add_child();
- child->set_field(1);
-
- vector<Matcher> output;
- translateFieldMatcher(matcher1, &output);
-
- ASSERT_EQ((size_t)1, output.size());
-
- const auto& matcher12 = output[0];
- EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag());
- EXPECT_EQ((int32_t)0x02010001, matcher12.mMatcher.getField());
- EXPECT_EQ((int32_t)0xff7f7f7f, matcher12.mMask);
-}
-
-TEST(AtomMatcherTest, TestFilter_ALL) {
- FieldMatcher matcher1;
- matcher1.set_field(10);
- FieldMatcher* child = matcher1.add_child();
- child->set_field(1);
- child->set_position(Position::ALL);
-
- child->add_child()->set_field(1);
- child->add_child()->set_field(2);
-
- child = matcher1.add_child();
- child->set_field(2);
-
- vector<Matcher> matchers;
- translateFieldMatcher(matcher1, &matchers);
-
- std::vector<int> attributionUids = {1111, 2222, 3333};
- std::vector<string> attributionTags = {"location1", "location2", "location3"};
-
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value");
- HashableDimensionKey output;
-
- filterValues(matchers, event.getValues(), &output);
-
- ASSERT_EQ((size_t)7, output.getValues().size());
- EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
- EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
- EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
- EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
-
- EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField());
- EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value);
- EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField());
- EXPECT_EQ("location2", output.getValues()[3].mValue.str_value);
-
- EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField());
- EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value);
- EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField());
- EXPECT_EQ("location3", output.getValues()[5].mValue.str_value);
-
- EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField());
- EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
-}
-
-TEST(AtomMatcherTest, TestSubDimension) {
- HashableDimensionKey dim;
-
- int pos1[] = {1, 1, 1};
- int pos2[] = {1, 1, 2};
- int pos3[] = {1, 1, 3};
- int pos4[] = {2, 0, 0};
- Field field1(10, pos1, 2);
- Field field2(10, pos2, 2);
-
- Field field3(10, pos3, 2);
- Field field4(10, pos4, 0);
-
- Value value1((int32_t)10025);
- Value value2("tag");
-
- Value value11((int32_t)10026);
- Value value22("tag2");
-
- dim.addValue(FieldValue(field1, value1));
- dim.addValue(FieldValue(field2, value2));
-
- HashableDimensionKey subDim1;
- subDim1.addValue(FieldValue(field1, value1));
-
- HashableDimensionKey subDim2;
- subDim1.addValue(FieldValue(field2, value2));
-
- EXPECT_TRUE(dim.contains(dim));
- EXPECT_TRUE(dim.contains(subDim1));
- EXPECT_TRUE(dim.contains(subDim2));
-
- HashableDimensionKey subDim3;
- subDim3.addValue(FieldValue(field1, value11));
- EXPECT_FALSE(dim.contains(subDim3));
-
- HashableDimensionKey subDim4;
- // Empty dimension is always a sub dimension of other dimensions
- EXPECT_TRUE(dim.contains(subDim4));
-}
-
-TEST(AtomMatcherTest, TestMetric2ConditionLink) {
- std::vector<int> attributionUids = {1111, 2222, 3333};
- std::vector<string> attributionTags = {"location1", "location2", "location3"};
-
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event, 10 /*atomId*/, 12345, attributionUids, attributionTags, "some value");
-
- FieldMatcher whatMatcher;
- whatMatcher.set_field(10);
- FieldMatcher* child11 = whatMatcher.add_child();
- child11->set_field(1);
- child11->set_position(Position::ANY);
- child11 = child11->add_child();
- child11->set_field(1);
-
- FieldMatcher conditionMatcher;
- conditionMatcher.set_field(27);
- FieldMatcher* child2 = conditionMatcher.add_child();
- child2->set_field(2);
- child2->set_position(Position::LAST);
-
- child2 = child2->add_child();
- child2->set_field(2);
-
- Metric2Condition link;
-
- translateFieldMatcher(whatMatcher, &link.metricFields);
- translateFieldMatcher(conditionMatcher, &link.conditionFields);
-
- ASSERT_EQ((size_t)1, link.metricFields.size());
- EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField());
- EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask);
- EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag());
-
- ASSERT_EQ((size_t)1, link.conditionFields.size());
- EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField());
- EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask);
- EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
-}
-
-TEST(AtomMatcherTest, TestWriteDimensionPath) {
- for (auto position : {Position::ANY, Position::ALL, Position::FIRST, Position::LAST}) {
- FieldMatcher matcher1;
- matcher1.set_field(10);
- FieldMatcher* child = matcher1.add_child();
- child->set_field(2);
- child->set_position(position);
- child->add_child()->set_field(1);
- child->add_child()->set_field(3);
-
- child = matcher1.add_child();
- child->set_field(4);
-
- child = matcher1.add_child();
- child->set_field(6);
- child->add_child()->set_field(2);
-
- vector<Matcher> matchers;
- translateFieldMatcher(matcher1, &matchers);
-
- android::util::ProtoOutputStream protoOut;
- writeDimensionPathToProto(matchers, &protoOut);
-
- vector<uint8_t> outData;
- outData.resize(protoOut.size());
- size_t pos = 0;
- sp<ProtoReader> reader = protoOut.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- DimensionsValue result;
- ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
-
- EXPECT_EQ(10, result.field());
- EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case());
- ASSERT_EQ(3, result.value_tuple().dimensions_value_size());
-
- const auto& dim1 = result.value_tuple().dimensions_value(0);
- EXPECT_EQ(2, dim1.field());
- ASSERT_EQ(2, dim1.value_tuple().dimensions_value_size());
-
- const auto& dim11 = dim1.value_tuple().dimensions_value(0);
- EXPECT_EQ(1, dim11.field());
-
- const auto& dim12 = dim1.value_tuple().dimensions_value(1);
- EXPECT_EQ(3, dim12.field());
-
- const auto& dim2 = result.value_tuple().dimensions_value(1);
- EXPECT_EQ(4, dim2.field());
-
- const auto& dim3 = result.value_tuple().dimensions_value(2);
- EXPECT_EQ(6, dim3.field());
- ASSERT_EQ(1, dim3.value_tuple().dimensions_value_size());
- const auto& dim31 = dim3.value_tuple().dimensions_value(0);
- EXPECT_EQ(2, dim31.field());
- }
-}
-
-void checkAttributionNodeInDimensionsValueParcel(StatsDimensionsValueParcel& attributionNodeParcel,
- int32_t nodeDepthInAttributionChain,
- int32_t uid, string tag) {
- EXPECT_EQ(attributionNodeParcel.field, nodeDepthInAttributionChain /*position at depth 1*/);
- ASSERT_EQ(attributionNodeParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
- ASSERT_EQ(attributionNodeParcel.tupleValue.size(), 2);
-
- StatsDimensionsValueParcel uidParcel = attributionNodeParcel.tupleValue[0];
- EXPECT_EQ(uidParcel.field, 1 /*position at depth 2*/);
- EXPECT_EQ(uidParcel.valueType, STATS_DIMENSIONS_VALUE_INT_TYPE);
- EXPECT_EQ(uidParcel.intValue, uid);
-
- StatsDimensionsValueParcel tagParcel = attributionNodeParcel.tupleValue[1];
- EXPECT_EQ(tagParcel.field, 2 /*position at depth 2*/);
- EXPECT_EQ(tagParcel.valueType, STATS_DIMENSIONS_VALUE_STRING_TYPE);
- EXPECT_EQ(tagParcel.stringValue, tag);
-}
-
-// Test conversion of a HashableDimensionKey into a StatsDimensionValueParcel
-TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
- int atomId = 10;
- // First four fields form an attribution chain
- int pos1[] = {1, 1, 1};
- int pos2[] = {1, 1, 2};
- int pos3[] = {1, 2, 1};
- int pos4[] = {1, 2, 2};
- int pos5[] = {2, 1, 1};
-
- Field field1(atomId, pos1, /*depth=*/2);
- Field field2(atomId, pos2, /*depth=*/2);
- Field field3(atomId, pos3, /*depth=*/2);
- Field field4(atomId, pos4, /*depth=*/2);
- Field field5(atomId, pos5, /*depth=*/0);
-
- Value value1((int32_t)1);
- Value value2("string2");
- Value value3((int32_t)3);
- Value value4("string4");
- Value value5((float)5.0);
-
- HashableDimensionKey dimensionKey;
- dimensionKey.addValue(FieldValue(field1, value1));
- dimensionKey.addValue(FieldValue(field2, value2));
- dimensionKey.addValue(FieldValue(field3, value3));
- dimensionKey.addValue(FieldValue(field4, value4));
- dimensionKey.addValue(FieldValue(field5, value5));
-
- StatsDimensionsValueParcel rootParcel = dimensionKey.toStatsDimensionsValueParcel();
- EXPECT_EQ(rootParcel.field, atomId);
- ASSERT_EQ(rootParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
- ASSERT_EQ(rootParcel.tupleValue.size(), 2);
-
- // Check that attribution chain is populated correctly
- StatsDimensionsValueParcel attributionChainParcel = rootParcel.tupleValue[0];
- EXPECT_EQ(attributionChainParcel.field, 1 /*position at depth 0*/);
- ASSERT_EQ(attributionChainParcel.valueType, STATS_DIMENSIONS_VALUE_TUPLE_TYPE);
- ASSERT_EQ(attributionChainParcel.tupleValue.size(), 2);
- checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[0],
- /*nodeDepthInAttributionChain=*/1,
- value1.int_value, value2.str_value);
- checkAttributionNodeInDimensionsValueParcel(attributionChainParcel.tupleValue[1],
- /*nodeDepthInAttributionChain=*/2,
- value3.int_value, value4.str_value);
-
- // Check that the float is populated correctly
- StatsDimensionsValueParcel floatParcel = rootParcel.tupleValue[1];
- EXPECT_EQ(floatParcel.field, 2 /*position at depth 0*/);
- EXPECT_EQ(floatParcel.valueType, STATS_DIMENSIONS_VALUE_FLOAT_TYPE);
- EXPECT_EQ(floatParcel.floatValue, value5.float_value);
-}
-
-TEST(AtomMatcherTest, TestWriteDimensionToProto) {
- HashableDimensionKey dim;
- int pos1[] = {1, 1, 1};
- int pos2[] = {1, 1, 2};
- int pos3[] = {1, 1, 3};
- int pos4[] = {2, 0, 0};
- Field field1(10, pos1, 2);
- Field field2(10, pos2, 2);
- Field field3(10, pos3, 2);
- Field field4(10, pos4, 0);
-
- Value value1((int32_t)10025);
- Value value2("tag");
- Value value3((int32_t)987654);
- Value value4((int32_t)99999);
-
- dim.addValue(FieldValue(field1, value1));
- dim.addValue(FieldValue(field2, value2));
- dim.addValue(FieldValue(field3, value3));
- dim.addValue(FieldValue(field4, value4));
-
- android::util::ProtoOutputStream protoOut;
- writeDimensionToProto(dim, nullptr /* include strings */, &protoOut);
-
- vector<uint8_t> outData;
- outData.resize(protoOut.size());
- size_t pos = 0;
- sp<ProtoReader> reader = protoOut.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- DimensionsValue result;
- ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
- EXPECT_EQ(10, result.field());
- EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case());
- ASSERT_EQ(2, result.value_tuple().dimensions_value_size());
-
- const auto& dim1 = result.value_tuple().dimensions_value(0);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, dim1.value_case());
- ASSERT_EQ(3, dim1.value_tuple().dimensions_value_size());
-
- const auto& dim11 = dim1.value_tuple().dimensions_value(0);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim11.value_case());
- EXPECT_EQ(10025, dim11.value_int());
-
- const auto& dim12 = dim1.value_tuple().dimensions_value(1);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim12.value_case());
- EXPECT_EQ("tag", dim12.value_str());
-
- const auto& dim13 = dim1.value_tuple().dimensions_value(2);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim13.value_case());
- EXPECT_EQ(987654, dim13.value_int());
-
- const auto& dim2 = result.value_tuple().dimensions_value(1);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim2.value_case());
- EXPECT_EQ(99999, dim2.value_int());
-}
-
-TEST(AtomMatcherTest, TestWriteDimensionLeafNodesToProto) {
- HashableDimensionKey dim;
- int pos1[] = {1, 1, 1};
- int pos2[] = {1, 1, 2};
- int pos3[] = {1, 1, 3};
- int pos4[] = {2, 0, 0};
- Field field1(10, pos1, 2);
- Field field2(10, pos2, 2);
- Field field3(10, pos3, 2);
- Field field4(10, pos4, 0);
-
- Value value1((int32_t)10025);
- Value value2("tag");
- Value value3((int32_t)987654);
- Value value4((int64_t)99999);
-
- dim.addValue(FieldValue(field1, value1));
- dim.addValue(FieldValue(field2, value2));
- dim.addValue(FieldValue(field3, value3));
- dim.addValue(FieldValue(field4, value4));
-
- android::util::ProtoOutputStream protoOut;
- writeDimensionLeafNodesToProto(dim, 1, nullptr /* include strings */, &protoOut);
-
- vector<uint8_t> outData;
- outData.resize(protoOut.size());
- size_t pos = 0;
- sp<ProtoReader> reader = protoOut.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- DimensionsValueTuple result;
- ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
- ASSERT_EQ(4, result.dimensions_value_size());
-
- const auto& dim1 = result.dimensions_value(0);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim1.value_case());
- EXPECT_EQ(10025, dim1.value_int());
-
- const auto& dim2 = result.dimensions_value(1);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueStr, dim2.value_case());
- EXPECT_EQ("tag", dim2.value_str());
-
- const auto& dim3 = result.dimensions_value(2);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim3.value_case());
- EXPECT_EQ(987654, dim3.value_int());
-
- const auto& dim4 = result.dimensions_value(3);
- EXPECT_EQ(DimensionsValue::ValueCase::kValueLong, dim4.value_case());
- EXPECT_EQ(99999, dim4.value_long());
-}
-
-TEST(AtomMatcherTest, TestWriteAtomToProto) {
- std::vector<int> attributionUids = {1111, 2222};
- std::vector<string> attributionTags = {"location1", "location2"};
-
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event, 4 /*atomId*/, 12345, attributionUids, attributionTags, 999);
-
- android::util::ProtoOutputStream protoOutput;
- writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput);
-
- vector<uint8_t> outData;
- outData.resize(protoOutput.size());
- size_t pos = 0;
- sp<ProtoReader> reader = protoOutput.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- Atom result;
- ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
- EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case());
- const auto& atom = result.ble_scan_result_received();
- ASSERT_EQ(2, atom.attribution_node_size());
- EXPECT_EQ(1111, atom.attribution_node(0).uid());
- EXPECT_EQ("location1", atom.attribution_node(0).tag());
- EXPECT_EQ(2222, atom.attribution_node(1).uid());
- EXPECT_EQ("location2", atom.attribution_node(1).tag());
- EXPECT_EQ(999, atom.num_results());
-}
-
-/*
- * Test two Matchers is not a subset of one Matcher.
- * Test one Matcher is subset of two Matchers.
- */
-TEST(AtomMatcherTest, TestSubsetDimensions1) {
- // Initialize first set of matchers
- FieldMatcher matcher1;
- matcher1.set_field(10);
-
- FieldMatcher* child = matcher1.add_child();
- child->set_field(1);
- child->set_position(Position::ALL);
- child->add_child()->set_field(1);
- child->add_child()->set_field(2);
-
- vector<Matcher> matchers1;
- translateFieldMatcher(matcher1, &matchers1);
- ASSERT_EQ(2, matchers1.size());
-
- // Initialize second set of matchers
- FieldMatcher matcher2;
- matcher2.set_field(10);
-
- child = matcher2.add_child();
- child->set_field(1);
- child->set_position(Position::ALL);
- child->add_child()->set_field(1);
-
- vector<Matcher> matchers2;
- translateFieldMatcher(matcher2, &matchers2);
- ASSERT_EQ(1, matchers2.size());
-
- EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
- EXPECT_TRUE(subsetDimensions(matchers2, matchers1));
-}
-/*
- * Test not a subset with one matching Matcher, one non-matching Matcher.
- */
-TEST(AtomMatcherTest, TestSubsetDimensions2) {
- // Initialize first set of matchers
- FieldMatcher matcher1;
- matcher1.set_field(10);
-
- FieldMatcher* child = matcher1.add_child();
- child->set_field(1);
-
- child = matcher1.add_child();
- child->set_field(2);
-
- vector<Matcher> matchers1;
- translateFieldMatcher(matcher1, &matchers1);
-
- // Initialize second set of matchers
- FieldMatcher matcher2;
- matcher2.set_field(10);
-
- child = matcher2.add_child();
- child->set_field(1);
-
- child = matcher2.add_child();
- child->set_field(3);
-
- vector<Matcher> matchers2;
- translateFieldMatcher(matcher2, &matchers2);
-
- EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
-}
-
-/*
- * Test not a subset if parent field is not equal.
- */
-TEST(AtomMatcherTest, TestSubsetDimensions3) {
- // Initialize first set of matchers
- FieldMatcher matcher1;
- matcher1.set_field(10);
-
- FieldMatcher* child = matcher1.add_child();
- child->set_field(1);
-
- vector<Matcher> matchers1;
- translateFieldMatcher(matcher1, &matchers1);
-
- // Initialize second set of matchers
- FieldMatcher matcher2;
- matcher2.set_field(5);
-
- child = matcher2.add_child();
- child->set_field(1);
-
- vector<Matcher> matchers2;
- translateFieldMatcher(matcher2, &matchers2);
-
- EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
-}
-
-/*
- * Test is subset with two matching Matchers.
- */
-TEST(AtomMatcherTest, TestSubsetDimensions4) {
- // Initialize first set of matchers
- FieldMatcher matcher1;
- matcher1.set_field(10);
-
- FieldMatcher* child = matcher1.add_child();
- child->set_field(1);
-
- child = matcher1.add_child();
- child->set_field(2);
-
- vector<Matcher> matchers1;
- translateFieldMatcher(matcher1, &matchers1);
-
- // Initialize second set of matchers
- FieldMatcher matcher2;
- matcher2.set_field(10);
-
- child = matcher2.add_child();
- child->set_field(1);
-
- child = matcher2.add_child();
- child->set_field(2);
-
- child = matcher2.add_child();
- child->set_field(3);
-
- vector<Matcher> matchers2;
- translateFieldMatcher(matcher2, &matchers2);
-
- EXPECT_TRUE(subsetDimensions(matchers1, matchers2));
- EXPECT_FALSE(subsetDimensions(matchers2, matchers1));
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/HashableDimensionKey_test.cpp b/cmds/statsd/tests/HashableDimensionKey_test.cpp
deleted file mode 100644
index 29adcd08a7b8..000000000000
--- a/cmds/statsd/tests/HashableDimensionKey_test.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2020 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 "src/HashableDimensionKey.h"
-
-#include <gtest/gtest.h>
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "statsd_test_util.h"
-
-#ifdef __ANDROID__
-
-using android::util::ProtoReader;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Test that #containsLinkedStateValues returns false when the whatKey is
- * smaller than the primaryKey.
- */
-TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_WhatKeyTooSmall) {
- std::vector<Metric2State> mMetric2StateLinks;
-
- int32_t uid1 = 1000;
- HashableDimensionKey whatKey = DEFAULT_DIMENSION_KEY;
- HashableDimensionKey primaryKey;
- getUidProcessKey(uid1, &primaryKey);
-
- EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks,
- UID_PROCESS_STATE_ATOM_ID));
-}
-
-/**
- * Test that #containsLinkedStateValues returns false when the linked values
- * are not equal.
- */
-TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_UnequalLinkedValues) {
- int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
-
- FieldMatcher whatMatcher;
- whatMatcher.set_field(util::OVERLAY_STATE_CHANGED);
- FieldMatcher* child11 = whatMatcher.add_child();
- child11->set_field(1);
-
- FieldMatcher stateMatcher;
- stateMatcher.set_field(stateAtomId);
- FieldMatcher* child21 = stateMatcher.add_child();
- child21->set_field(1);
-
- std::vector<Metric2State> mMetric2StateLinks;
- Metric2State ms;
- ms.stateAtomId = stateAtomId;
- translateFieldMatcher(whatMatcher, &ms.metricFields);
- translateFieldMatcher(stateMatcher, &ms.stateFields);
- mMetric2StateLinks.push_back(ms);
-
- int32_t uid1 = 1000;
- int32_t uid2 = 1001;
- HashableDimensionKey whatKey;
- getOverlayKey(uid2, "package", &whatKey);
- HashableDimensionKey primaryKey;
- getUidProcessKey(uid1, &primaryKey);
-
- EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
-}
-
-/**
- * Test that #containsLinkedStateValues returns false when there is no link
- * between the key values.
- */
-TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_MissingMetric2StateLinks) {
- int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
-
- std::vector<Metric2State> mMetric2StateLinks;
-
- int32_t uid1 = 1000;
- HashableDimensionKey whatKey;
- getOverlayKey(uid1, "package", &whatKey);
- HashableDimensionKey primaryKey;
- getUidProcessKey(uid1, &primaryKey);
-
- EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
-}
-
-/**
- * Test that #containsLinkedStateValues returns true when the key values are
- * linked and equal.
- */
-TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_AllConditionsMet) {
- int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
-
- FieldMatcher whatMatcher;
- whatMatcher.set_field(util::OVERLAY_STATE_CHANGED);
- FieldMatcher* child11 = whatMatcher.add_child();
- child11->set_field(1);
-
- FieldMatcher stateMatcher;
- stateMatcher.set_field(stateAtomId);
- FieldMatcher* child21 = stateMatcher.add_child();
- child21->set_field(1);
-
- std::vector<Metric2State> mMetric2StateLinks;
- Metric2State ms;
- ms.stateAtomId = stateAtomId;
- translateFieldMatcher(whatMatcher, &ms.metricFields);
- translateFieldMatcher(stateMatcher, &ms.stateFields);
- mMetric2StateLinks.push_back(ms);
-
- int32_t uid1 = 1000;
- HashableDimensionKey whatKey;
- getOverlayKey(uid1, "package", &whatKey);
- HashableDimensionKey primaryKey;
- getUidProcessKey(uid1, &primaryKey);
-
- EXPECT_TRUE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
deleted file mode 100644
index 92cd04f37ee0..000000000000
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ /dev/null
@@ -1,809 +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.
-
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include "annotations.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "matchers/matcher_util.h"
-#include "stats_event.h"
-#include "stats_log_util.h"
-#include "stats_util.h"
-#include "statsd_test_util.h"
-
-using namespace android::os::statsd;
-using std::unordered_map;
-using std::vector;
-
-const int32_t TAG_ID = 123;
-const int32_t TAG_ID_2 = 28; // hardcoded tag of atom with uid field
-const int FIELD_ID_1 = 1;
-const int FIELD_ID_2 = 2;
-const int FIELD_ID_3 = 2;
-
-const int ATTRIBUTION_UID_FIELD_ID = 1;
-const int ATTRIBUTION_TAG_FIELD_ID = 2;
-
-
-#ifdef __ANDROID__
-
-namespace {
-
-void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const int32_t value) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- AStatsEvent_writeInt32(statsEvent, value);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const float floatValue) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- AStatsEvent_writeFloat(statsEvent, floatValue);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const string& name) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- AStatsEvent_writeString(statsEvent, name.c_str());
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-void makeIntWithBoolAnnotationLogEvent(LogEvent* logEvent, const int32_t atomId,
- const int32_t field, const uint8_t annotationId,
- const bool annotationValue) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_writeInt32(statsEvent, field);
- AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags, const string& name) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, name.c_str());
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const bool bool1, const bool bool2) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
- AStatsEvent_writeBool(statsEvent, bool1);
- AStatsEvent_writeBool(statsEvent, bool2);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-} // anonymous namespace
-
-TEST(AtomMatcherTest, TestSimpleMatcher) {
- sp<UidMap> uidMap = new UidMap();
-
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
-
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeIntLogEvent(&event, TAG_ID, 0, 11);
-
- // Test
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // Wrong tag id.
- simpleMatcher->set_atom_id(TAG_ID + 1);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestAttributionMatcher) {
- sp<UidMap> uidMap = new UidMap();
- std::vector<int> attributionUids = {1111, 2222, 3333};
- std::vector<string> attributionTags = {"location1", "location2", "location3"};
-
- // Set up the log event.
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
-
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
-
- // Match first node.
- auto attributionMatcher = simpleMatcher->add_field_value_matcher();
- attributionMatcher->set_field(FIELD_ID_1);
- attributionMatcher->set_position(Position::FIRST);
- attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
- ATTRIBUTION_TAG_FIELD_ID);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "tag");
-
- auto fieldMatcher = simpleMatcher->add_field_value_matcher();
- fieldMatcher->set_field(FIELD_ID_2);
- fieldMatcher->set_eq_string("some value");
-
- // Tag not matched.
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // Match last node.
- attributionMatcher->set_position(Position::LAST);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // Match any node.
- attributionMatcher->set_position(Position::ANY);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location4");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // Attribution match but primitive field not match.
- attributionMatcher->set_position(Position::ANY);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "location2");
- fieldMatcher->set_eq_string("wrong value");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- fieldMatcher->set_eq_string("some value");
-
- // Uid match.
- attributionMatcher->set_position(Position::ANY);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
- ATTRIBUTION_UID_FIELD_ID);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg0");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- uidMap->updateMap(
- 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
- {android::String16("v1"), android::String16("v1"), android::String16("v2"),
- android::String16("v1"), android::String16("v2")},
- {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
- android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
- {android::String16(""), android::String16(""), android::String16(""),
- android::String16(""), android::String16("")});
-
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg0");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- attributionMatcher->set_position(Position::FIRST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg0");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- attributionMatcher->set_position(Position::LAST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg0");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // Uid + tag.
- attributionMatcher->set_position(Position::ANY);
- attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
- ATTRIBUTION_TAG_FIELD_ID);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg0");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location2");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- attributionMatcher->set_position(Position::FIRST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg0");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- attributionMatcher->set_position(Position::LAST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg0");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg2");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
- "pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
- "location1");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestUidFieldMatcher) {
- sp<UidMap> uidMap = new UidMap();
- uidMap->updateMap(
- 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
- {android::String16("v1"), android::String16("v1"), android::String16("v2"),
- android::String16("v1"), android::String16("v2")},
- {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
- android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
- {android::String16(""), android::String16(""), android::String16(""),
- android::String16(""), android::String16("")});
-
- // Set up matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
- simpleMatcher->add_field_value_matcher()->set_field(1);
- simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
-
- // Make event without is_uid annotation.
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeIntLogEvent(&event1, TAG_ID, 0, 1111);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
-
- // Make event with is_uid annotation.
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true);
-
- // Event has is_uid annotation, so mapping from uid to package name occurs.
- simpleMatcher->set_atom_id(TAG_ID_2);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
-
- // Event has is_uid annotation, but uid maps to different package name.
- simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
-}
-
-TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
- sp<UidMap> uidMap = new UidMap();
- uidMap->updateMap(
- 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
- {android::String16("v1"), android::String16("v1"), android::String16("v2"),
- android::String16("v1"), android::String16("v2")},
- {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
- android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
- {android::String16(""), android::String16(""), android::String16(""),
- android::String16(""), android::String16("")});
-
- std::vector<int> attributionUids = {1111, 2222, 3333, 1066};
- std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
-
- // Set up the event
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
-
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
-
- // Match first node.
- auto attributionMatcher = simpleMatcher->add_field_value_matcher();
- attributionMatcher->set_field(FIELD_ID_1);
- attributionMatcher->set_position(Position::FIRST);
- attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
- ATTRIBUTION_UID_FIELD_ID);
- auto neqStringList = attributionMatcher->mutable_matches_tuple()
- ->mutable_field_value_matcher(0)
- ->mutable_neq_any_string();
- neqStringList->add_str_value("pkg2");
- neqStringList->add_str_value("pkg3");
-
- auto fieldMatcher = simpleMatcher->add_field_value_matcher();
- fieldMatcher->set_field(FIELD_ID_2);
- fieldMatcher->set_eq_string("some value");
-
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- neqStringList->Clear();
- neqStringList->add_str_value("pkg1");
- neqStringList->add_str_value("pkg3");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- attributionMatcher->set_position(Position::ANY);
- neqStringList->Clear();
- neqStringList->add_str_value("maps.com");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- neqStringList->Clear();
- neqStringList->add_str_value("PkG3");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- attributionMatcher->set_position(Position::LAST);
- neqStringList->Clear();
- neqStringList->add_str_value("AID_STATSD");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
- sp<UidMap> uidMap = new UidMap();
- uidMap->updateMap(
- 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
- {android::String16("v1"), android::String16("v1"), android::String16("v2"),
- android::String16("v1"), android::String16("v2")},
- {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
- android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
- {android::String16(""), android::String16(""), android::String16(""),
- android::String16(""), android::String16("")});
-
- std::vector<int> attributionUids = {1067, 2222, 3333, 1066};
- std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
-
- // Set up the event
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
-
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
-
- // Match first node.
- auto attributionMatcher = simpleMatcher->add_field_value_matcher();
- attributionMatcher->set_field(FIELD_ID_1);
- attributionMatcher->set_position(Position::FIRST);
- attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
- ATTRIBUTION_UID_FIELD_ID);
- auto eqStringList = attributionMatcher->mutable_matches_tuple()
- ->mutable_field_value_matcher(0)
- ->mutable_eq_any_string();
- eqStringList->add_str_value("AID_ROOT");
- eqStringList->add_str_value("AID_INCIDENTD");
-
- auto fieldMatcher = simpleMatcher->add_field_value_matcher();
- fieldMatcher->set_field(FIELD_ID_2);
- fieldMatcher->set_eq_string("some value");
-
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- attributionMatcher->set_position(Position::ANY);
- eqStringList->Clear();
- eqStringList->add_str_value("AID_STATSD");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- eqStringList->Clear();
- eqStringList->add_str_value("pkg1");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- auto normalStringField = fieldMatcher->mutable_eq_any_string();
- normalStringField->add_str_value("some value123");
- normalStringField->add_str_value("some value");
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- normalStringField->Clear();
- normalStringField->add_str_value("AID_STATSD");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- eqStringList->Clear();
- eqStringList->add_str_value("maps.com");
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestBoolMatcher) {
- sp<UidMap> uidMap = new UidMap();
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
- auto keyValue1 = simpleMatcher->add_field_value_matcher();
- keyValue1->set_field(FIELD_ID_1);
- auto keyValue2 = simpleMatcher->add_field_value_matcher();
- keyValue2->set_field(FIELD_ID_2);
-
- // Set up the event
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeBoolLogEvent(&event, TAG_ID, 0, true, false);
-
- // Test
- keyValue1->set_eq_bool(true);
- keyValue2->set_eq_bool(false);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- keyValue1->set_eq_bool(false);
- keyValue2->set_eq_bool(false);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- keyValue1->set_eq_bool(false);
- keyValue2->set_eq_bool(true);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- keyValue1->set_eq_bool(true);
- keyValue2->set_eq_bool(true);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestStringMatcher) {
- sp<UidMap> uidMap = new UidMap();
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
- auto keyValue = simpleMatcher->add_field_value_matcher();
- keyValue->set_field(FIELD_ID_1);
- keyValue->set_eq_string("some value");
-
- // Set up the event
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeStringLogEvent(&event, TAG_ID, 0, "some value");
-
- // Test
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
- sp<UidMap> uidMap = new UidMap();
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
- auto keyValue1 = simpleMatcher->add_field_value_matcher();
- keyValue1->set_field(FIELD_ID_1);
- auto keyValue2 = simpleMatcher->add_field_value_matcher();
- keyValue2->set_field(FIELD_ID_2);
-
- // Set up the event
- LogEvent event(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event, TAG_ID, 0, 2, 3);
-
- // Test
- keyValue1->set_eq_int(2);
- keyValue2->set_eq_int(3);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- keyValue1->set_eq_int(2);
- keyValue2->set_eq_int(4);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- keyValue1->set_eq_int(4);
- keyValue2->set_eq_int(3);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestIntComparisonMatcher) {
- sp<UidMap> uidMap = new UidMap();
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-
- simpleMatcher->set_atom_id(TAG_ID);
- auto keyValue = simpleMatcher->add_field_value_matcher();
- keyValue->set_field(FIELD_ID_1);
-
- // Set up the event
- LogEvent event(/*uid=*/0, /*pid=*/0);
- makeIntLogEvent(&event, TAG_ID, 0, 11);
-
- // Test
-
- // eq_int
- keyValue->set_eq_int(10);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_eq_int(11);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_eq_int(12);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // lt_int
- keyValue->set_lt_int(10);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_lt_int(11);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_lt_int(12);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // lte_int
- keyValue->set_lte_int(10);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_lte_int(11);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_lte_int(12);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // gt_int
- keyValue->set_gt_int(10);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_gt_int(11);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_gt_int(12);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-
- // gte_int
- keyValue->set_gte_int(10);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_gte_int(11);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue->set_gte_int(12);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-}
-
-TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
- sp<UidMap> uidMap = new UidMap();
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_atom_id(TAG_ID);
-
- auto keyValue = simpleMatcher->add_field_value_matcher();
- keyValue->set_field(FIELD_ID_1);
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f);
- keyValue->set_lt_float(10.0);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
-
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f);
- keyValue->set_gt_float(10.0);
- EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
-
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f);
- EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
-}
-
-// Helper for the composite matchers.
-void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) {
- simpleMatcher->set_atom_id(tag);
- auto keyValue = simpleMatcher->add_field_value_matcher();
- keyValue->set_field(key);
- keyValue->set_eq_int(val);
-}
-
-TEST(AtomMatcherTest, TestAndMatcher) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::AND;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
- children.push_back(2);
-
- vector<MatchingState> matcherResults;
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
- matcherResults.push_back(MatchingState::kMatched);
-
- EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-
- matcherResults.clear();
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kMatched);
-
- EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
-}
-
-TEST(AtomMatcherTest, TestOrMatcher) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::OR;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
- children.push_back(2);
-
- vector<MatchingState> matcherResults;
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
- matcherResults.push_back(MatchingState::kMatched);
-
- EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
-
- matcherResults.clear();
- matcherResults.push_back(MatchingState::kNotMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
-
- EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-}
-
-TEST(AtomMatcherTest, TestNotMatcher) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::NOT;
-
- vector<int> children;
- children.push_back(0);
-
- vector<MatchingState> matcherResults;
- matcherResults.push_back(MatchingState::kMatched);
-
- EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-
- matcherResults.clear();
- matcherResults.push_back(MatchingState::kNotMatched);
- EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
-}
-
-TEST(AtomMatcherTest, TestNandMatcher) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::NAND;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
-
- vector<MatchingState> matcherResults;
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
-
- EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
-
- matcherResults.clear();
- matcherResults.push_back(MatchingState::kNotMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
- EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
-
- matcherResults.clear();
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kMatched);
- EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-}
-
-TEST(AtomMatcherTest, TestNorMatcher) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::NOR;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
-
- vector<MatchingState> matcherResults;
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
-
- EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-
- matcherResults.clear();
- matcherResults.push_back(MatchingState::kNotMatched);
- matcherResults.push_back(MatchingState::kNotMatched);
- EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
-
- matcherResults.clear();
- matcherResults.push_back(MatchingState::kMatched);
- matcherResults.push_back(MatchingState::kMatched);
- EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-}
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
deleted file mode 100644
index bde59f497b19..000000000000
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ /dev/null
@@ -1,371 +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.
-
-#include "src/logd/LogEvent.h"
-
-#include <gtest/gtest.h>
-
-#include "frameworks/proto_logging/stats/atoms.pb.h"
-#include "frameworks/proto_logging/stats/enums/stats/launcher/launcher.pb.h"
-#include "log/log_event_list.h"
-#include "stats_event.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::string;
-using std::vector;
-using util::ProtoOutputStream;
-using util::ProtoReader;
-
-namespace {
-
-Field getField(int32_t tag, const vector<int32_t>& pos, int32_t depth, const vector<bool>& last) {
- Field f(tag, (int32_t*)pos.data(), depth);
-
- // For loop starts at 1 because the last field at depth 0 is not decorated.
- for (int i = 1; i < depth; i++) {
- if (last[i]) f.decorateLastPos(i);
- }
-
- return f;
-}
-
-void createIntWithBoolAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId,
- bool annotationValue) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
- AStatsEvent_writeInt32(statsEvent, 10);
- AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- EXPECT_TRUE(logEvent->parseBuffer(buf, size));
-
- AStatsEvent_release(statsEvent);
-}
-
-void createIntWithIntAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId,
- int annotationValue) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
- AStatsEvent_writeInt32(statsEvent, 10);
- AStatsEvent_addInt32Annotation(statsEvent, annotationId, annotationValue);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- EXPECT_TRUE(logEvent->parseBuffer(buf, size));
-
- AStatsEvent_release(statsEvent);
-}
-
-} // anonymous namespace
-
-TEST(LogEventTest, TestPrimitiveParsing) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
- AStatsEvent_writeInt32(event, 10);
- AStatsEvent_writeInt64(event, 0x123456789);
- AStatsEvent_writeFloat(event, 2.0);
- AStatsEvent_writeBool(event, true);
- AStatsEvent_build(event);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(event, &size);
-
- LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
- EXPECT_TRUE(logEvent.parseBuffer(buf, size));
-
- EXPECT_EQ(100, logEvent.GetTagId());
- EXPECT_EQ(1000, logEvent.GetUid());
- EXPECT_EQ(1001, logEvent.GetPid());
- EXPECT_FALSE(logEvent.hasAttributionChain());
-
- const vector<FieldValue>& values = logEvent.getValues();
- ASSERT_EQ(4, values.size());
-
- const FieldValue& int32Item = values[0];
- Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false});
- EXPECT_EQ(expectedField, int32Item.mField);
- EXPECT_EQ(Type::INT, int32Item.mValue.getType());
- EXPECT_EQ(10, int32Item.mValue.int_value);
-
- const FieldValue& int64Item = values[1];
- expectedField = getField(100, {2, 1, 1}, 0, {false, false, false});
- EXPECT_EQ(expectedField, int64Item.mField);
- EXPECT_EQ(Type::LONG, int64Item.mValue.getType());
- EXPECT_EQ(0x123456789, int64Item.mValue.long_value);
-
- const FieldValue& floatItem = values[2];
- expectedField = getField(100, {3, 1, 1}, 0, {false, false, false});
- EXPECT_EQ(expectedField, floatItem.mField);
- EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType());
- EXPECT_EQ(2.0, floatItem.mValue.float_value);
-
- const FieldValue& boolItem = values[3];
- expectedField = getField(100, {4, 1, 1}, 0, {true, false, false});
- EXPECT_EQ(expectedField, boolItem.mField);
- EXPECT_EQ(Type::INT, boolItem.mValue.getType()); // FieldValue does not support boolean type
- EXPECT_EQ(1, boolItem.mValue.int_value);
-
- AStatsEvent_release(event);
-}
-
-TEST(LogEventTest, TestStringAndByteArrayParsing) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
- string str = "test";
- AStatsEvent_writeString(event, str.c_str());
- AStatsEvent_writeByteArray(event, (uint8_t*)str.c_str(), str.length());
- AStatsEvent_build(event);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(event, &size);
-
- LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
- EXPECT_TRUE(logEvent.parseBuffer(buf, size));
-
- EXPECT_EQ(100, logEvent.GetTagId());
- EXPECT_EQ(1000, logEvent.GetUid());
- EXPECT_EQ(1001, logEvent.GetPid());
- EXPECT_FALSE(logEvent.hasAttributionChain());
-
- const vector<FieldValue>& values = logEvent.getValues();
- ASSERT_EQ(2, values.size());
-
- const FieldValue& stringItem = values[0];
- Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false});
- EXPECT_EQ(expectedField, stringItem.mField);
- EXPECT_EQ(Type::STRING, stringItem.mValue.getType());
- EXPECT_EQ(str, stringItem.mValue.str_value);
-
- const FieldValue& storageItem = values[1];
- expectedField = getField(100, {2, 1, 1}, 0, {true, false, false});
- EXPECT_EQ(expectedField, storageItem.mField);
- EXPECT_EQ(Type::STORAGE, storageItem.mValue.getType());
- vector<uint8_t> expectedValue = {'t', 'e', 's', 't'};
- EXPECT_EQ(expectedValue, storageItem.mValue.storage_value);
-
- AStatsEvent_release(event);
-}
-
-TEST(LogEventTest, TestEmptyString) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
- string empty = "";
- AStatsEvent_writeString(event, empty.c_str());
- AStatsEvent_build(event);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(event, &size);
-
- LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
- EXPECT_TRUE(logEvent.parseBuffer(buf, size));
-
- EXPECT_EQ(100, logEvent.GetTagId());
- EXPECT_EQ(1000, logEvent.GetUid());
- EXPECT_EQ(1001, logEvent.GetPid());
- EXPECT_FALSE(logEvent.hasAttributionChain());
-
- const vector<FieldValue>& values = logEvent.getValues();
- ASSERT_EQ(1, values.size());
-
- const FieldValue& item = values[0];
- Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false});
- EXPECT_EQ(expectedField, item.mField);
- EXPECT_EQ(Type::STRING, item.mValue.getType());
- EXPECT_EQ(empty, item.mValue.str_value);
-
- AStatsEvent_release(event);
-}
-
-TEST(LogEventTest, TestByteArrayWithNullCharacter) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
- uint8_t message[] = {'\t', 'e', '\0', 's', 't'};
- AStatsEvent_writeByteArray(event, message, 5);
- AStatsEvent_build(event);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(event, &size);
-
- LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
- EXPECT_TRUE(logEvent.parseBuffer(buf, size));
-
- EXPECT_EQ(100, logEvent.GetTagId());
- EXPECT_EQ(1000, logEvent.GetUid());
- EXPECT_EQ(1001, logEvent.GetPid());
-
- const vector<FieldValue>& values = logEvent.getValues();
- ASSERT_EQ(1, values.size());
-
- const FieldValue& item = values[0];
- Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false});
- EXPECT_EQ(expectedField, item.mField);
- EXPECT_EQ(Type::STORAGE, item.mValue.getType());
- vector<uint8_t> expectedValue(message, message + 5);
- EXPECT_EQ(expectedValue, item.mValue.storage_value);
-
- AStatsEvent_release(event);
-}
-
-TEST(LogEventTest, TestAttributionChain) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
-
- string tag1 = "tag1";
- string tag2 = "tag2";
-
- uint32_t uids[] = {1001, 1002};
- const char* tags[] = {tag1.c_str(), tag2.c_str()};
-
- AStatsEvent_writeAttributionChain(event, uids, tags, 2);
- AStatsEvent_build(event);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(event, &size);
-
- LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
- EXPECT_TRUE(logEvent.parseBuffer(buf, size));
-
- EXPECT_EQ(100, logEvent.GetTagId());
- EXPECT_EQ(1000, logEvent.GetUid());
- EXPECT_EQ(1001, logEvent.GetPid());
-
- const vector<FieldValue>& values = logEvent.getValues();
- ASSERT_EQ(4, values.size()); // 2 per attribution node
-
- std::pair<int, int> attrIndexRange;
- EXPECT_TRUE(logEvent.hasAttributionChain(&attrIndexRange));
- EXPECT_EQ(0, attrIndexRange.first);
- EXPECT_EQ(3, attrIndexRange.second);
-
- // Check first attribution node
- const FieldValue& uid1Item = values[0];
- Field expectedField = getField(100, {1, 1, 1}, 2, {true, false, false});
- EXPECT_EQ(expectedField, uid1Item.mField);
- EXPECT_EQ(Type::INT, uid1Item.mValue.getType());
- EXPECT_EQ(1001, uid1Item.mValue.int_value);
-
- const FieldValue& tag1Item = values[1];
- expectedField = getField(100, {1, 1, 2}, 2, {true, false, true});
- EXPECT_EQ(expectedField, tag1Item.mField);
- EXPECT_EQ(Type::STRING, tag1Item.mValue.getType());
- EXPECT_EQ(tag1, tag1Item.mValue.str_value);
-
- // Check second attribution nodes
- const FieldValue& uid2Item = values[2];
- expectedField = getField(100, {1, 2, 1}, 2, {true, true, false});
- EXPECT_EQ(expectedField, uid2Item.mField);
- EXPECT_EQ(Type::INT, uid2Item.mValue.getType());
- EXPECT_EQ(1002, uid2Item.mValue.int_value);
-
- const FieldValue& tag2Item = values[3];
- expectedField = getField(100, {1, 2, 2}, 2, {true, true, true});
- EXPECT_EQ(expectedField, tag2Item.mField);
- EXPECT_EQ(Type::STRING, tag2Item.mValue.getType());
- EXPECT_EQ(tag2, tag2Item.mValue.str_value);
-
- AStatsEvent_release(event);
-}
-
-TEST(LogEventTest, TestAnnotationIdIsUid) {
- LogEvent event(/*uid=*/0, /*pid=*/0);
- createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_IS_UID, true);
-
- const vector<FieldValue>& values = event.getValues();
- ASSERT_EQ(values.size(), 1);
- EXPECT_EQ(event.getUidFieldIndex(), 0);
-}
-
-TEST(LogEventTest, TestAnnotationIdStateNested) {
- LogEvent event(/*uid=*/0, /*pid=*/0);
- createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_STATE_NESTED, true);
-
- const vector<FieldValue>& values = event.getValues();
- ASSERT_EQ(values.size(), 1);
- EXPECT_TRUE(values[0].mAnnotations.isNested());
-}
-
-TEST(LogEventTest, TestPrimaryFieldAnnotation) {
- LogEvent event(/*uid=*/0, /*pid=*/0);
- createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_PRIMARY_FIELD, true);
-
- const vector<FieldValue>& values = event.getValues();
- ASSERT_EQ(values.size(), 1);
- EXPECT_TRUE(values[0].mAnnotations.isPrimaryField());
-}
-
-TEST(LogEventTest, TestExclusiveStateAnnotation) {
- LogEvent event(/*uid=*/0, /*pid=*/0);
- createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_EXCLUSIVE_STATE, true);
-
- const vector<FieldValue>& values = event.getValues();
- ASSERT_EQ(values.size(), 1);
- EXPECT_TRUE(values[0].mAnnotations.isExclusiveState());
-}
-
-TEST(LogEventTest, TestPrimaryFieldFirstUidAnnotation) {
- // Event has 10 ints and then an attribution chain
- int numInts = 10;
- int firstUidInChainIndex = numInts;
- string tag1 = "tag1";
- string tag2 = "tag2";
- uint32_t uids[] = {1001, 1002};
- const char* tags[] = {tag1.c_str(), tag2.c_str()};
-
- // Construct AStatsEvent
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, 100);
- for (int i = 0; i < numInts; i++) {
- AStatsEvent_writeInt32(statsEvent, 10);
- }
- AStatsEvent_writeAttributionChain(statsEvent, uids, tags, 2);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
- AStatsEvent_build(statsEvent);
-
- // Construct LogEvent
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- LogEvent logEvent(/*uid=*/0, /*pid=*/0);
- EXPECT_TRUE(logEvent.parseBuffer(buf, size));
- AStatsEvent_release(statsEvent);
-
- // Check annotation
- const vector<FieldValue>& values = logEvent.getValues();
- ASSERT_EQ(values.size(), numInts + 4);
- EXPECT_TRUE(values[firstUidInChainIndex].mAnnotations.isPrimaryField());
-}
-
-TEST(LogEventTest, TestResetStateAnnotation) {
- int32_t resetState = 10;
- LogEvent event(/*uid=*/0, /*pid=*/0);
- createIntWithIntAnnotationLogEvent(&event, ANNOTATION_ID_TRIGGER_STATE_RESET, resetState);
-
- const vector<FieldValue>& values = event.getValues();
- ASSERT_EQ(values.size(), 1);
- EXPECT_EQ(event.getResetState(), resetState);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/LogReader_test.cpp b/cmds/statsd/tests/LogReader_test.cpp
deleted file mode 100644
index 7ce1d6a71c85..000000000000
--- a/cmds/statsd/tests/LogReader_test.cpp
+++ /dev/null
@@ -1,21 +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.
-
-#include <gtest/gtest.h>
-
-#include <stdio.h>
-
-TEST(LogReaderTest, TestNothingAtAll) {
- printf("yay!");
-}
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
deleted file mode 100644
index f05ec490dcee..000000000000
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ /dev/null
@@ -1,325 +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.
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <private/android_filesystem_config.h>
-#include <stdio.h>
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "metrics/metrics_test_helper.h"
-#include "src/condition/ConditionTracker.h"
-#include "src/matchers/AtomMatchingTracker.h"
-#include "src/metrics/CountMetricProducer.h"
-#include "src/metrics/GaugeMetricProducer.h"
-#include "src/metrics/MetricProducer.h"
-#include "src/metrics/ValueMetricProducer.h"
-#include "src/metrics/parsing_utils/metrics_manager_util.h"
-#include "src/state/StateManager.h"
-#include "statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using android::os::statsd::Predicate;
-using std::map;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-const ConfigKey kConfigKey(0, 12345);
-
-const long timeBaseSec = 1000;
-
-StatsdConfig buildGoodConfig() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- combination->add_matcher(StringToId("SCREEN_IS_OFF"));
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("SCREEN_IS_ON"));
- metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
- return config;
-}
-
-set<int32_t> unionSet(const vector<set<int32_t>> sets) {
- set<int32_t> toRet;
- for (const set<int32_t>& s : sets) {
- toRet.insert(s.begin(), s.end());
- }
- return toRet;
-}
-} // anonymous namespace
-
-TEST(MetricsManagerTest, TestLogSources) {
- string app1 = "app1";
- set<int32_t> app1Uids = {1111, 11111};
- string app2 = "app2";
- set<int32_t> app2Uids = {2222};
- string app3 = "app3";
- set<int32_t> app3Uids = {3333, 1111};
-
- map<string, set<int32_t>> pkgToUids;
- pkgToUids[app1] = app1Uids;
- pkgToUids[app2] = app2Uids;
- pkgToUids[app3] = app3Uids;
-
- int32_t atom1 = 10, atom2 = 20, atom3 = 30;
- sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getAppUid(_))
- .Times(4)
- .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) {
- const auto& it = pkgToUids.find(pkg);
- if (it != pkgToUids.end()) {
- return it->second;
- }
- return set<int32_t>();
- }));
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1);
- EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1);
-
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
-
- StatsdConfig config;
- config.add_allowed_log_source("AID_SYSTEM");
- config.add_allowed_log_source(app1);
- config.add_default_pull_packages("AID_SYSTEM");
- config.add_default_pull_packages("AID_ROOT");
-
- const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_ROOT};
-
- PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages();
- pullAtomPackages->set_atom_id(atom1);
- pullAtomPackages->add_packages(app1);
- pullAtomPackages->add_packages(app3);
-
- pullAtomPackages = config.add_pull_atom_packages();
- pullAtomPackages->set_atom_id(atom2);
- pullAtomPackages->add_packages(app2);
- pullAtomPackages->add_packages("AID_STATSD");
-
- MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
- pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
- EXPECT_TRUE(metricsManager.isConfigValid());
-
- EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_SYSTEM));
- EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app1));
- EXPECT_THAT(metricsManager.mAllowedLogSources,
- ContainerEq(unionSet(vector<set<int32_t>>({app1Uids, {AID_SYSTEM}}))));
- EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids));
-
- vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1);
- EXPECT_THAT(atom1Uids,
- UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids})));
-
- vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2);
- EXPECT_THAT(atom2Uids,
- UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_STATSD}})));
-
- vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3);
- EXPECT_THAT(atom3Uids, UnorderedElementsAreArray(defaultPullUids));
-}
-
-TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate) {
- string app1 = "app1";
- set<int32_t> app1Uids = {1111, 11111};
- string app2 = "app2";
- set<int32_t> app2Uids = {2222};
- string app3 = "app3";
- set<int32_t> app3Uids = {3333, 1111};
-
- map<string, set<int32_t>> pkgToUids;
- pkgToUids[app1] = app1Uids;
- pkgToUids[app2] = app2Uids;
- pkgToUids[app3] = app3Uids;
-
- int32_t atom1 = 10, atom2 = 20, atom3 = 30;
- sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getAppUid(_))
- .Times(8)
- .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) {
- const auto& it = pkgToUids.find(pkg);
- if (it != pkgToUids.end()) {
- return it->second;
- }
- return set<int32_t>();
- }));
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1);
- EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1);
-
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
-
- StatsdConfig config;
- config.add_allowed_log_source("AID_SYSTEM");
- config.add_allowed_log_source(app1);
- config.add_default_pull_packages("AID_SYSTEM");
- config.add_default_pull_packages("AID_ROOT");
-
- PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages();
- pullAtomPackages->set_atom_id(atom1);
- pullAtomPackages->add_packages(app1);
- pullAtomPackages->add_packages(app3);
-
- pullAtomPackages = config.add_pull_atom_packages();
- pullAtomPackages->set_atom_id(atom2);
- pullAtomPackages->add_packages(app2);
- pullAtomPackages->add_packages("AID_STATSD");
-
- MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
- pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
- EXPECT_TRUE(metricsManager.isConfigValid());
-
- // Update with new allowed log sources.
- StatsdConfig newConfig;
- newConfig.add_allowed_log_source("AID_ROOT");
- newConfig.add_allowed_log_source(app2);
- newConfig.add_default_pull_packages("AID_SYSTEM");
- newConfig.add_default_pull_packages("AID_STATSD");
-
- pullAtomPackages = newConfig.add_pull_atom_packages();
- pullAtomPackages->set_atom_id(atom2);
- pullAtomPackages->add_packages(app1);
- pullAtomPackages->add_packages(app3);
-
- pullAtomPackages = newConfig.add_pull_atom_packages();
- pullAtomPackages->set_atom_id(atom3);
- pullAtomPackages->add_packages(app2);
- pullAtomPackages->add_packages("AID_ADB");
-
- metricsManager.updateConfig(newConfig, timeBaseSec, timeBaseSec, anomalyAlarmMonitor,
- periodicAlarmMonitor);
- EXPECT_TRUE(metricsManager.isConfigValid());
-
- EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_ROOT));
- EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app2));
- EXPECT_THAT(metricsManager.mAllowedLogSources,
- ContainerEq(unionSet(vector<set<int32_t>>({app2Uids, {AID_ROOT}}))));
- const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_STATSD};
- EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids));
-
- vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1);
- EXPECT_THAT(atom1Uids, UnorderedElementsAreArray(defaultPullUids));
-
- vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2);
- EXPECT_THAT(atom2Uids,
- UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids})));
-
- vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3);
- EXPECT_THAT(atom3Uids,
- UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_ADB}})));
-}
-
-TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) {
- sp<UidMap> uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
-
- StatsdConfig config;
- config.add_whitelisted_atom_ids(3);
- config.add_whitelisted_atom_ids(4);
-
- MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
- pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
-
- LogEvent event(0 /* uid */, 0 /* pid */);
- CreateNoValuesLogEvent(&event, 10 /* atom id */, 0 /* timestamp */);
- EXPECT_FALSE(metricsManager.checkLogCredentials(event));
-
- CreateNoValuesLogEvent(&event, 3 /* atom id */, 0 /* timestamp */);
- EXPECT_TRUE(metricsManager.checkLogCredentials(event));
-
- CreateNoValuesLogEvent(&event, 4 /* atom id */, 0 /* timestamp */);
- EXPECT_TRUE(metricsManager.checkLogCredentials(event));
-}
-
-TEST(MetricsManagerTest, TestWhitelistedAtomStateTracker) {
- sp<UidMap> uidMap;
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
-
- StatsdConfig config = buildGoodConfig();
- config.add_allowed_log_source("AID_SYSTEM");
- config.add_whitelisted_atom_ids(3);
- config.add_whitelisted_atom_ids(4);
-
- State state;
- state.set_id(1);
- state.set_atom_id(3);
-
- *config.add_state() = state;
-
- config.mutable_count_metric(0)->add_slice_by_state(state.id());
-
- StateManager::getInstance().clear();
-
- MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
- pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
-
- EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount());
- EXPECT_FALSE(metricsManager.isConfigValid());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
deleted file mode 100644
index 1409b621fdf6..000000000000
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ /dev/null
@@ -1,1886 +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.
-
-#include "StatsLogProcessor.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include "StatsService.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "guardrail/StatsdStats.h"
-#include "logd/LogEvent.h"
-#include "packages/UidMap.h"
-#include "statslog_statsdtest.h"
-#include "storage/StorageManager.h"
-#include "tests/statsd_test_util.h"
-
-using namespace android;
-using namespace testing;
-using ::ndk::SharedRefBase;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoOutputStream;
-
-#ifdef __ANDROID__
-
-/**
- * Mock MetricsManager (ByteSize() is called).
- */
-class MockMetricsManager : public MetricsManager {
-public:
- MockMetricsManager()
- : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
- new StatsPullerManager(),
- new AlarmMonitor(10,
- [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
- [](const shared_ptr<IStatsCompanionService>&) {}),
- new AlarmMonitor(10,
- [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
- [](const shared_ptr<IStatsCompanionService>&) {})) {
- }
-
- MOCK_METHOD0(byteSize, size_t());
-
- MOCK_METHOD1(dropData, void(const int64_t dropTimeNs));
-};
-
-TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- // Construct the processor with a no-op sendBroadcast function that does nothing.
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor, 0,
- [](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) {return true;});
-
- MockMetricsManager mockMetricsManager;
-
- ConfigKey key(100, 12345);
- // Expect only the first flush to trigger a check for byte size since the last two are
- // rate-limited.
- EXPECT_CALL(mockMetricsManager, byteSize()).Times(1);
- p.flushIfNecessaryLocked(key, mockMetricsManager);
- p.flushIfNecessaryLocked(key, mockMetricsManager);
- p.flushIfNecessaryLocked(key, mockMetricsManager);
-}
-
-TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- int broadcastCount = 0;
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) {
- broadcastCount++;
- return true;
- },
- [](const int&, const vector<int64_t>&) {return true;});
-
- MockMetricsManager mockMetricsManager;
-
- ConfigKey key(100, 12345);
- EXPECT_CALL(mockMetricsManager, byteSize())
- .Times(1)
- .WillRepeatedly(::testing::Return(int(
- StatsdStats::kMaxMetricsBytesPerConfig * .95)));
-
- // Expect only one broadcast despite always returning a size that should trigger broadcast.
- p.flushIfNecessaryLocked(key, mockMetricsManager);
- EXPECT_EQ(1, broadcastCount);
-
- // b/73089712
- // This next call to flush should not trigger a broadcast.
- // p.mLastByteSizeTimes.clear(); // Force another check for byte size.
- // p.flushIfNecessaryLocked(2, key, mockMetricsManager);
- // EXPECT_EQ(1, broadcastCount);
-}
-
-TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- int broadcastCount = 0;
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) {
- broadcastCount++;
- return true;
- },
- [](const int&, const vector<int64_t>&) {return true;});
-
- MockMetricsManager mockMetricsManager;
-
- ConfigKey key(100, 12345);
- EXPECT_CALL(mockMetricsManager, byteSize())
- .Times(1)
- .WillRepeatedly(::testing::Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
-
- EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1);
-
- // Expect to call the onDumpReport and skip the broadcast.
- p.flushIfNecessaryLocked(key, mockMetricsManager);
- EXPECT_EQ(0, broadcastCount);
-}
-
-StatsdConfig MakeConfig(bool includeMetric) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- if (includeMetric) {
- auto appCrashMatcher = CreateProcessCrashAtomMatcher();
- *config.add_atom_matcher() = appCrashMatcher;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(StringToId("AppCrashes"));
- countMetric->set_what(appCrashMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- }
- return config;
-}
-
-TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
- // Setup simple config key corresponding to empty config.
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
- {String16("p1"), String16("p2")}, {String16(""), String16("")});
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- int broadcastCount = 0;
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) {
- broadcastCount++;
- return true;
- },
- [](const int&, const vector<int64_t>&) {return true;});
- ConfigKey key(3, 4);
- StatsdConfig config = MakeConfig(true);
- p.OnConfigUpdated(0, key, config);
-
- // Expect to get no metrics, but snapshot specified above in uidmap.
- vector<uint8_t> bytes;
- p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes);
-
- ConfigMetricsReportList output;
- output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_TRUE(output.reports_size() > 0);
- auto uidmap = output.reports(0).uid_map();
- EXPECT_TRUE(uidmap.snapshots_size() > 0);
- ASSERT_EQ(2, uidmap.snapshots(0).package_info_size());
-}
-
-TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) {
- // Setup simple config key corresponding to empty config.
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
- {String16("p1"), String16("p2")}, {String16(""), String16("")});
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- int broadcastCount = 0;
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) {
- broadcastCount++;
- return true;
- },
- [](const int&, const vector<int64_t>&) {return true;});
- ConfigKey key(3, 4);
- StatsdConfig config = MakeConfig(false);
- p.OnConfigUpdated(0, key, config);
-
- // Expect to get no metrics, but snapshot specified above in uidmap.
- vector<uint8_t> bytes;
- p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes);
-
- ConfigMetricsReportList output;
- output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_TRUE(output.reports_size() > 0);
- EXPECT_FALSE(output.reports(0).has_uid_map());
-}
-
-TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) {
- // Setup simple config key corresponding to empty config.
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- int broadcastCount = 0;
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [&broadcastCount](const ConfigKey& key) {
- broadcastCount++;
- return true;
- },
- [](const int&, const vector<int64_t>&) {return true;});
- ConfigKey key(3, 4);
- StatsdConfig config;
- auto annotation = config.add_annotation();
- annotation->set_field_int64(1);
- annotation->set_field_int32(2);
- config.add_allowed_log_source("AID_ROOT");
- p.OnConfigUpdated(1, key, config);
-
- // Expect to get no metrics, but snapshot specified above in uidmap.
- vector<uint8_t> bytes;
- p.onDumpReport(key, 1, false, true, ADB_DUMP, FAST, &bytes);
-
- ConfigMetricsReportList output;
- output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_TRUE(output.reports_size() > 0);
- auto report = output.reports(0);
- ASSERT_EQ(1, report.annotation_size());
- EXPECT_EQ(1, report.annotation(0).field_int64());
- EXPECT_EQ(2, report.annotation(0).field_int32());
-}
-
-TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
- // Setup a simple config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = wakelockAcquireMatcher;
-
- auto countMetric = config.add_count_metric();
- countMetric->set_id(123456);
- countMetric->set_what(wakelockAcquireMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
-
- ConfigKey cfgKey;
- sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
-
- std::vector<int> attributionUids = {111};
- std::vector<string> attributionTags = {"App1"};
- std::unique_ptr<LogEvent> event =
- CreateAcquireWakelockEvent(2 /*timestamp*/, attributionUids, attributionTags, "wl1");
- processor->OnLogEvent(event.get());
-
- vector<uint8_t> bytes;
- ConfigMetricsReportList output;
-
- // Dump report WITHOUT erasing data.
- processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST,
- &bytes);
- output.ParseFromArray(bytes.data(), bytes.size());
- ASSERT_EQ(output.reports_size(), 1);
- ASSERT_EQ(output.reports(0).metrics_size(), 1);
- ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
-
- // Dump report WITH erasing data. There should be data since we didn't previously erase it.
- processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
- output.ParseFromArray(bytes.data(), bytes.size());
- ASSERT_EQ(output.reports_size(), 1);
- ASSERT_EQ(output.reports(0).metrics_size(), 1);
- ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
-
- // Dump report again. There should be no data since we erased it.
- processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
- output.ParseFromArray(bytes.data(), bytes.size());
- // We don't care whether statsd has a report, as long as it has no count metrics in it.
- bool noData = output.reports_size() == 0 || output.reports(0).metrics_size() == 0 ||
- output.reports(0).metrics(0).count_metrics().data_size() == 0;
- EXPECT_TRUE(noData);
-}
-
-TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) {
- // Setup simple config key corresponding to empty config.
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- StatsLogProcessor p(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) { return true; });
- ConfigKey key(3, 4);
- StatsdConfig config = MakeConfig(false);
- p.OnConfigUpdated(0, key, config);
- EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
-
- config.add_default_pull_packages("AID_STATSD");
- p.OnConfigUpdated(5, key, config);
- EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
-
- p.OnConfigRemoved(key);
- EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
-}
-
-TEST(StatsLogProcessorTest, InvalidConfigRemoved) {
- // Setup simple config key corresponding to empty config.
- StatsdStats::getInstance().reset();
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
- {String16("p1"), String16("p2")}, {String16(""), String16("")});
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) {return true;});
- ConfigKey key(3, 4);
- StatsdConfig config = MakeConfig(true);
- p.OnConfigUpdated(0, key, config);
- EXPECT_EQ(1, p.mMetricsManagers.size());
- EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end());
- // Cannot assert the size of mConfigStats since it is static and does not get cleared on reset.
- EXPECT_NE(StatsdStats::getInstance().mConfigStats.end(),
- StatsdStats::getInstance().mConfigStats.find(key));
- EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size());
-
- StatsdConfig invalidConfig = MakeConfig(true);
- invalidConfig.clear_allowed_log_source();
- p.OnConfigUpdated(0, key, invalidConfig);
- EXPECT_EQ(0, p.mMetricsManagers.size());
- // The current configs should not contain the invalid config.
- EXPECT_EQ(StatsdStats::getInstance().mConfigStats.end(),
- StatsdStats::getInstance().mConfigStats.find(key));
- // Both "config" and "invalidConfig" should be in the icebox.
- EXPECT_EQ(2, StatsdStats::getInstance().mIceBox.size());
-
-}
-
-
-TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
- int uid = 1111;
-
- // Setup a simple config, no activation
- StatsdConfig config1;
- int64_t cfgId1 = 12341;
- config1.set_id(cfgId1);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
- *config1.add_atom_matcher() = wakelockAcquireMatcher;
-
- long metricId1 = 1234561;
- long metricId2 = 1234562;
- auto countMetric1 = config1.add_count_metric();
- countMetric1->set_id(metricId1);
- countMetric1->set_what(wakelockAcquireMatcher.id());
- countMetric1->set_bucket(FIVE_MINUTES);
-
- auto countMetric2 = config1.add_count_metric();
- countMetric2->set_id(metricId2);
- countMetric2->set_what(wakelockAcquireMatcher.id());
- countMetric2->set_bucket(FIVE_MINUTES);
-
- ConfigKey cfgKey1(uid, cfgId1);
-
- // Add another config, with two metrics, one with activation
- StatsdConfig config2;
- int64_t cfgId2 = 12342;
- config2.set_id(cfgId2);
- config2.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config2.add_atom_matcher() = wakelockAcquireMatcher;
-
- long metricId3 = 1234561;
- long metricId4 = 1234562;
-
- auto countMetric3 = config2.add_count_metric();
- countMetric3->set_id(metricId3);
- countMetric3->set_what(wakelockAcquireMatcher.id());
- countMetric3->set_bucket(FIVE_MINUTES);
-
- auto countMetric4 = config2.add_count_metric();
- countMetric4->set_id(metricId4);
- countMetric4->set_what(wakelockAcquireMatcher.id());
- countMetric4->set_bucket(FIVE_MINUTES);
-
- auto metric3Activation = config2.add_metric_activation();
- metric3Activation->set_metric_id(metricId3);
- metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
- auto metric3ActivationTrigger = metric3Activation->add_event_activation();
- metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
- metric3ActivationTrigger->set_ttl_seconds(100);
-
- ConfigKey cfgKey2(uid, cfgId2);
-
- // Add another config, with two metrics, both with activations
- StatsdConfig config3;
- int64_t cfgId3 = 12343;
- config3.set_id(cfgId3);
- config3.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config3.add_atom_matcher() = wakelockAcquireMatcher;
-
- long metricId5 = 1234565;
- long metricId6 = 1234566;
- auto countMetric5 = config3.add_count_metric();
- countMetric5->set_id(metricId5);
- countMetric5->set_what(wakelockAcquireMatcher.id());
- countMetric5->set_bucket(FIVE_MINUTES);
-
- auto countMetric6 = config3.add_count_metric();
- countMetric6->set_id(metricId6);
- countMetric6->set_what(wakelockAcquireMatcher.id());
- countMetric6->set_bucket(FIVE_MINUTES);
-
- auto metric5Activation = config3.add_metric_activation();
- metric5Activation->set_metric_id(metricId5);
- metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
- auto metric5ActivationTrigger = metric5Activation->add_event_activation();
- metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
- metric5ActivationTrigger->set_ttl_seconds(100);
-
- auto metric6Activation = config3.add_metric_activation();
- metric6Activation->set_metric_id(metricId6);
- metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
- auto metric6ActivationTrigger = metric6Activation->add_event_activation();
- metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
- metric6ActivationTrigger->set_ttl_seconds(200);
-
- ConfigKey cfgKey3(uid, cfgId3);
-
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- vector<int64_t> activeConfigsBroadcast;
-
- long timeBase1 = 1;
- int broadcastCount = 0;
- StatsLogProcessor processor(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, timeBase1,
- [](const ConfigKey& key) { return true; },
- [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
- broadcastCount++;
- EXPECT_EQ(broadcastUid, uid);
- activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
- activeConfigs.end());
- return true;
- });
-
- processor.OnConfigUpdated(1, cfgKey1, config1);
- processor.OnConfigUpdated(2, cfgKey2, config2);
- processor.OnConfigUpdated(3, cfgKey3, config3);
-
- ASSERT_EQ(3, processor.mMetricsManagers.size());
-
- // Expect the first config and both metrics in it to be active.
- auto it = processor.mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor.mMetricsManagers.end());
- auto& metricsManager1 = it->second;
- EXPECT_TRUE(metricsManager1->isActive());
-
- auto metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer1 = *metricIt;
- EXPECT_TRUE(metricProducer1->isActive());
-
- metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer2 = *metricIt;
- EXPECT_TRUE(metricProducer2->isActive());
-
- // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
- it = processor.mMetricsManagers.find(cfgKey2);
- EXPECT_TRUE(it != processor.mMetricsManagers.end());
- auto& metricsManager2 = it->second;
- EXPECT_TRUE(metricsManager2->isActive());
-
- metricIt = metricsManager2->mAllMetricProducers.begin();
- for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId3) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
- auto& metricProducer3 = *metricIt;
- EXPECT_FALSE(metricProducer3->isActive());
-
- metricIt = metricsManager2->mAllMetricProducers.begin();
- for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId4) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
- auto& metricProducer4 = *metricIt;
- EXPECT_TRUE(metricProducer4->isActive());
-
- // Expect the third config and both metrics in it to be inactive.
- it = processor.mMetricsManagers.find(cfgKey3);
- EXPECT_TRUE(it != processor.mMetricsManagers.end());
- auto& metricsManager3 = it->second;
- EXPECT_FALSE(metricsManager3->isActive());
-
- metricIt = metricsManager3->mAllMetricProducers.begin();
- for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId5) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
- auto& metricProducer5 = *metricIt;
- EXPECT_FALSE(metricProducer5->isActive());
-
- metricIt = metricsManager3->mAllMetricProducers.begin();
- for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId6) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
- auto& metricProducer6 = *metricIt;
- EXPECT_FALSE(metricProducer6->isActive());
-
- // No broadcast for active configs should have happened yet.
- EXPECT_EQ(broadcastCount, 0);
-
- // Activate all 3 metrics that were not active.
- std::vector<int> attributionUids = {111};
- std::vector<string> attributionTags = {"App1"};
- std::unique_ptr<LogEvent> event =
- CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
- processor.OnLogEvent(event.get());
-
- // Assert that all 3 configs are active.
- EXPECT_TRUE(metricsManager1->isActive());
- EXPECT_TRUE(metricsManager2->isActive());
- EXPECT_TRUE(metricsManager3->isActive());
-
- // A broadcast should have happened, and all 3 configs should be active in the broadcast.
- EXPECT_EQ(broadcastCount, 1);
- ASSERT_EQ(activeConfigsBroadcast.size(), 3);
- EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) !=
- activeConfigsBroadcast.end());
- EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) !=
- activeConfigsBroadcast.end());
- EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) !=
- activeConfigsBroadcast.end());
-
- // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
- int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- processor.SaveActiveConfigsToDisk(shutDownTime);
- const int64_t ttl3 = event->GetElapsedTimestampNs() +
- metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
- const int64_t ttl5 = event->GetElapsedTimestampNs() +
- metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
- const int64_t ttl6 = event->GetElapsedTimestampNs() +
- metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
-
- // Create a second StatsLogProcessor and push the same 3 configs.
- long timeBase2 = 1000;
- sp<StatsLogProcessor> processor2 =
- CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
- processor2->OnConfigUpdated(timeBase2, cfgKey2, config2);
- processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
-
- ASSERT_EQ(3, processor2->mMetricsManagers.size());
-
- // First config and both metrics are active.
- it = processor2->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1001 = it->second;
- EXPECT_TRUE(metricsManager1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1001 = *metricIt;
- EXPECT_TRUE(metricProducer1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1002 = *metricIt;
- EXPECT_TRUE(metricProducer1002->isActive());
-
- // Second config is active. Metric 3 is inactive, metric 4 is active.
- it = processor2->mMetricsManagers.find(cfgKey2);
- EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1002 = it->second;
- EXPECT_TRUE(metricsManager1002->isActive());
-
- metricIt = metricsManager1002->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId3) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
- auto& metricProducer1003 = *metricIt;
- EXPECT_FALSE(metricProducer1003->isActive());
-
- metricIt = metricsManager1002->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId4) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
- auto& metricProducer1004 = *metricIt;
- EXPECT_TRUE(metricProducer1004->isActive());
-
- // Config 3 is inactive. both metrics are inactive.
- it = processor2->mMetricsManagers.find(cfgKey3);
- EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1003 = it->second;
- EXPECT_FALSE(metricsManager1003->isActive());
- ASSERT_EQ(2, metricsManager1003->mAllMetricProducers.size());
-
- metricIt = metricsManager1003->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId5) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
- auto& metricProducer1005 = *metricIt;
- EXPECT_FALSE(metricProducer1005->isActive());
-
- metricIt = metricsManager1003->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId6) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
- auto& metricProducer1006 = *metricIt;
- EXPECT_FALSE(metricProducer1006->isActive());
-
- // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
- EXPECT_FALSE(metricProducer1003->isActive());
- const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
- EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
- EXPECT_EQ(0, activation1003->start_ns);
- EXPECT_FALSE(metricProducer1005->isActive());
- const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
- EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns);
- EXPECT_EQ(0, activation1005->start_ns);
- EXPECT_FALSE(metricProducer1006->isActive());
- const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
- EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns);
- EXPECT_EQ(0, activation1006->start_ns);
-
- processor2->LoadActiveConfigsFromDisk();
-
- // After loading activations from disk, assert that all 3 metrics are active.
- EXPECT_TRUE(metricProducer1003->isActive());
- EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns);
- EXPECT_TRUE(metricProducer1005->isActive());
- EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns);
- EXPECT_TRUE(metricProducer1006->isActive());
- EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns);
-
- // Make sure no more broadcasts have happened.
- EXPECT_EQ(broadcastCount, 1);
-}
-
-TEST(StatsLogProcessorTest, TestActivationOnBoot) {
- int uid = 1111;
-
- StatsdConfig config1;
- config1.set_id(12341);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
- *config1.add_atom_matcher() = wakelockAcquireMatcher;
-
- long metricId1 = 1234561;
- long metricId2 = 1234562;
- auto countMetric1 = config1.add_count_metric();
- countMetric1->set_id(metricId1);
- countMetric1->set_what(wakelockAcquireMatcher.id());
- countMetric1->set_bucket(FIVE_MINUTES);
-
- auto countMetric2 = config1.add_count_metric();
- countMetric2->set_id(metricId2);
- countMetric2->set_what(wakelockAcquireMatcher.id());
- countMetric2->set_bucket(FIVE_MINUTES);
-
- auto metric1Activation = config1.add_metric_activation();
- metric1Activation->set_metric_id(metricId1);
- metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
- auto metric1ActivationTrigger = metric1Activation->add_event_activation();
- metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
- metric1ActivationTrigger->set_ttl_seconds(100);
-
- ConfigKey cfgKey1(uid, 12341);
- long timeBase1 = 1;
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-
- ASSERT_EQ(1, processor->mMetricsManagers.size());
- auto it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
- auto& metricsManager1 = it->second;
- EXPECT_TRUE(metricsManager1->isActive());
-
- auto metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer1 = *metricIt;
- EXPECT_FALSE(metricProducer1->isActive());
-
- metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer2 = *metricIt;
- EXPECT_TRUE(metricProducer2->isActive());
-
- const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second;
- EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kNotActive, activation1->state);
-
- std::vector<int> attributionUids = {111};
- std::vector<string> attributionTags = {"App1"};
- std::unique_ptr<LogEvent> event =
- CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
- processor->OnLogEvent(event.get());
-
- EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1->state);
-
- int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- processor->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_FALSE(metricProducer1->isActive());
- const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC;
-
- long timeBase2 = 1000;
- sp<StatsLogProcessor> processor2 =
- CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-
- ASSERT_EQ(1, processor2->mMetricsManagers.size());
- it = processor2->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1001 = it->second;
- EXPECT_TRUE(metricsManager1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1001 = *metricIt;
- EXPECT_FALSE(metricProducer1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1002 = *metricIt;
- EXPECT_TRUE(metricProducer1002->isActive());
-
- const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second;
- EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
- EXPECT_EQ(0, activation1001->start_ns);
- EXPECT_EQ(kNotActive, activation1001->state);
-
- processor2->LoadActiveConfigsFromDisk();
-
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns);
- EXPECT_EQ(kActive, activation1001->state);
-}
-
-TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) {
- int uid = 1111;
-
- // Create config with 2 metrics:
- // Metric 1: Activate on boot with 2 activations
- // Metric 2: Always active
- StatsdConfig config1;
- config1.set_id(12341);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- *config1.add_atom_matcher() = wakelockAcquireMatcher;
- *config1.add_atom_matcher() = screenOnMatcher;
-
- long metricId1 = 1234561;
- long metricId2 = 1234562;
-
- auto countMetric1 = config1.add_count_metric();
- countMetric1->set_id(metricId1);
- countMetric1->set_what(wakelockAcquireMatcher.id());
- countMetric1->set_bucket(FIVE_MINUTES);
-
- auto countMetric2 = config1.add_count_metric();
- countMetric2->set_id(metricId2);
- countMetric2->set_what(wakelockAcquireMatcher.id());
- countMetric2->set_bucket(FIVE_MINUTES);
-
- auto metric1Activation = config1.add_metric_activation();
- metric1Activation->set_metric_id(metricId1);
- metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
- auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
- metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
- metric1ActivationTrigger1->set_ttl_seconds(100);
- auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
- metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
- metric1ActivationTrigger2->set_ttl_seconds(200);
-
- ConfigKey cfgKey1(uid, 12341);
- long timeBase1 = 1;
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-
- // Metric 1 is not active.
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor->mMetricsManagers.size());
- auto it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
- auto& metricsManager1 = it->second;
- EXPECT_TRUE(metricsManager1->isActive());
-
- auto metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer1 = *metricIt;
- EXPECT_FALSE(metricProducer1->isActive());
-
- metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer2 = *metricIt;
- EXPECT_TRUE(metricProducer2->isActive());
-
- int i = 0;
- for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kNotActive, activation1->state);
-
- i = 0;
- for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
- // }}}------------------------------------------------------------------------------
-
- // Trigger Activation 1 for Metric 1
- std::vector<int> attributionUids = {111};
- std::vector<string> attributionTags = {"App1"};
- std::unique_ptr<LogEvent> event =
- CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
- processor->OnLogEvent(event.get());
-
- // Metric 1 is not active; Activation 1 set to kActiveOnBoot
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1->state);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
-
- EXPECT_TRUE(metricProducer2->isActive());
- // }}}-----------------------------------------------------------------------------
-
- // Simulate shutdown by saving state to disk
- int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- processor->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_FALSE(metricProducer1->isActive());
- int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
-
- // Simulate device restarted state by creating new instance of StatsLogProcessor with the
- // same config.
- long timeBase2 = 1000;
- sp<StatsLogProcessor> processor2 =
- CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-
- // Metric 1 is not active.
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor2->mMetricsManagers.size());
- it = processor2->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1001 = it->second;
- EXPECT_TRUE(metricsManager1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1001 = *metricIt;
- EXPECT_FALSE(metricProducer1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1002 = *metricIt;
- EXPECT_TRUE(metricProducer1002->isActive());
-
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
- EXPECT_EQ(0, activation1001_1->start_ns);
- EXPECT_EQ(kNotActive, activation1001_1->state);
-
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
-
- const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
- // }}}-----------------------------------------------------------------------------------
-
- // Load saved state from disk.
- processor2->LoadActiveConfigsFromDisk();
-
- // Metric 1 active; Activation 1 is active, Activation 2 is not active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
-
- EXPECT_TRUE(metricProducer1002->isActive());
- // }}}--------------------------------------------------------------------------------
-
- // Trigger Activation 2 for Metric 1.
- auto screenOnEvent =
- CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
- processor2->OnLogEvent(screenOnEvent.get());
-
- // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1001_2->state);
-
- EXPECT_TRUE(metricProducer1002->isActive());
- // }}}---------------------------------------------------------------------------
-
- // Simulate shutdown by saving state to disk
- shutDownTime = timeBase2 + 50 * NS_PER_SEC;
- processor2->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_TRUE(metricProducer1002->isActive());
- ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
- int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC;
-
- // Simulate device restarted state by creating new instance of StatsLogProcessor with the
- // same config.
- long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
- sp<StatsLogProcessor> processor3 =
- CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
-
- // Metric 1 is not active.
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor3->mMetricsManagers.size());
- it = processor3->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor3->mMetricsManagers.end());
- auto& metricsManagerTimeBase3 = it->second;
- EXPECT_TRUE(metricsManagerTimeBase3->isActive());
-
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_1 = *metricIt;
- EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
-
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_2 = *metricIt;
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
-
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
-
- const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
-
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
- // }}}----------------------------------------------------------------------------------
-
- // Load saved state from disk.
- processor3->LoadActiveConfigsFromDisk();
-
- // Metric 1 active: Activation 1 is active, Activation 2 is active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_1->state);
- EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
-
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
- // }}}-------------------------------------------------------------------------------
-
- // Trigger Activation 2 for Metric 1 again.
- screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
- android::view::DISPLAY_STATE_ON);
- processor3->OnLogEvent(screenOnEvent.get());
-
- // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
- EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
-
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
- // }}}---------------------------------------------------------------------------
-
- // Simulate shutdown by saving state to disk.
- shutDownTime = timeBase3 + 500 * NS_PER_SEC;
- processor3->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_TRUE(metricProducer1002->isActive());
- ttl1 = timeBase3 + ttl1 - shutDownTime;
- ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
-
- // Simulate device restarted state by creating new instance of StatsLogProcessor with the
- // same config.
- long timeBase4 = timeBase3 + 600 * NS_PER_SEC;
- sp<StatsLogProcessor> processor4 =
- CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1);
-
- // Metric 1 is not active.
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor4->mMetricsManagers.size());
- it = processor4->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor4->mMetricsManagers.end());
- auto& metricsManagerTimeBase4 = it->second;
- EXPECT_TRUE(metricsManagerTimeBase4->isActive());
-
- metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
- auto& metricProducerTimeBase4_1 = *metricIt;
- EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
-
- metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
- auto& metricProducerTimeBase4_2 = *metricIt;
- EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
-
- i = 0;
- for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns);
- EXPECT_EQ(0, activationTimeBase4_1->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
-
- i = 0;
- for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) {
- if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
-
- const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns);
- EXPECT_EQ(0, activationTimeBase4_2->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
-
- EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
- // }}}----------------------------------------------------------------------------------
-
- // Load saved state from disk.
- processor4->LoadActiveConfigsFromDisk();
-
- // Metric 1 active: Activation 1 is not active, Activation 2 is not active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
- EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
- EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
-
- EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
- // }}}-------------------------------------------------------------------------------
-}
-
-TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) {
- int uid = 1111;
-
- // Create config with 2 metrics:
- // Metric 1: Activate on boot with 2 activations
- // Metric 2: Always active
- StatsdConfig config1;
- config1.set_id(12341);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- *config1.add_atom_matcher() = wakelockAcquireMatcher;
- *config1.add_atom_matcher() = screenOnMatcher;
-
- long metricId1 = 1234561;
- long metricId2 = 1234562;
-
- auto countMetric1 = config1.add_count_metric();
- countMetric1->set_id(metricId1);
- countMetric1->set_what(wakelockAcquireMatcher.id());
- countMetric1->set_bucket(FIVE_MINUTES);
-
- auto countMetric2 = config1.add_count_metric();
- countMetric2->set_id(metricId2);
- countMetric2->set_what(wakelockAcquireMatcher.id());
- countMetric2->set_bucket(FIVE_MINUTES);
-
- auto metric1Activation = config1.add_metric_activation();
- metric1Activation->set_metric_id(metricId1);
- metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
- auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
- metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
- metric1ActivationTrigger1->set_ttl_seconds(100);
- auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
- metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
- metric1ActivationTrigger2->set_ttl_seconds(200);
- metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-
- ConfigKey cfgKey1(uid, 12341);
- long timeBase1 = 1;
- sp<StatsLogProcessor> processor1 =
- CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-
- // Metric 1 is not active.
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor1->mMetricsManagers.size());
- auto it = processor1->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor1->mMetricsManagers.end());
- auto& metricsManager1 = it->second;
- EXPECT_TRUE(metricsManager1->isActive());
-
- ASSERT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
- // We assume that the index of a MetricProducer within the mAllMetricProducers
- // array follows the order in which metrics are added to the config.
- auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
- EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
- EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation
-
- auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
- EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
- EXPECT_TRUE(metricProducer1_2->isActive());
-
- ASSERT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
- // The key in mEventActivationMap is the index of the associated atom matcher. We assume
- // that matchers are indexed in the order that they are added to the config.
- const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
- EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
- EXPECT_EQ(0, activation1_1_1->start_ns);
- EXPECT_EQ(kNotActive, activation1_1_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
-
- const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
- EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
- EXPECT_EQ(0, activation1_1_2->start_ns);
- EXPECT_EQ(kNotActive, activation1_1_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
- // }}}------------------------------------------------------------------------------
-
- // Trigger Activation 1 for Metric 1
- std::vector<int> attributionUids = {111};
- std::vector<string> attributionTags = {"App1"};
- std::unique_ptr<LogEvent> event =
- CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
- processor1->OnLogEvent(event.get());
-
- // Metric 1 is not active; Activation 1 set to kActiveOnBoot
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_FALSE(metricProducer1_1->isActive());
- EXPECT_EQ(0, activation1_1_1->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
- EXPECT_EQ(0, activation1_1_2->start_ns);
- EXPECT_EQ(kNotActive, activation1_1_2->state);
-
- EXPECT_TRUE(metricProducer1_2->isActive());
- // }}}-----------------------------------------------------------------------------
-
- // Simulate shutdown by saving state to disk
- int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- processor1->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_FALSE(metricProducer1_1->isActive());
-
- // Simulate device restarted state by creating new instance of StatsLogProcessor with the
- // same config.
- long timeBase2 = 1000;
- sp<StatsLogProcessor> processor2 =
- CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-
- // Metric 1 is not active.
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor2->mMetricsManagers.size());
- it = processor2->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager2 = it->second;
- EXPECT_TRUE(metricsManager2->isActive());
-
- ASSERT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
- // We assume that the index of a MetricProducer within the mAllMetricProducers
- // array follows the order in which metrics are added to the config.
- auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
- EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
- EXPECT_FALSE(metricProducer2_1->isActive());
-
- auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
- EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
- EXPECT_TRUE(metricProducer2_2->isActive());
-
- ASSERT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
- // The key in mEventActivationMap is the index of the associated atom matcher. We assume
- // that matchers are indexed in the order that they are added to the config.
- const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
- EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
- EXPECT_EQ(0, activation2_1_1->start_ns);
- EXPECT_EQ(kNotActive, activation2_1_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
-
- const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
- EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
- EXPECT_EQ(0, activation2_1_2->start_ns);
- EXPECT_EQ(kNotActive, activation2_1_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
- // }}}-----------------------------------------------------------------------------------
-
- // Load saved state from disk.
- processor2->LoadActiveConfigsFromDisk();
-
- // Metric 1 active; Activation 1 is active, Activation 2 is not active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer2_1->isActive());
- int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
- EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
- EXPECT_EQ(kActive, activation2_1_1->state);
- EXPECT_EQ(0, activation2_1_2->start_ns);
- EXPECT_EQ(kNotActive, activation2_1_2->state);
-
- EXPECT_TRUE(metricProducer2_2->isActive());
- // }}}--------------------------------------------------------------------------------
-
- // Trigger Activation 2 for Metric 1.
- auto screenOnEvent =
- CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
- processor2->OnLogEvent(screenOnEvent.get());
-
- // Metric 1 active; Activation 1 is active, Activation 2 is active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer2_1->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
- EXPECT_EQ(kActive, activation2_1_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
- EXPECT_EQ(kActive, activation2_1_2->state);
-
- EXPECT_TRUE(metricProducer2_2->isActive());
- // }}}---------------------------------------------------------------------------
-
- // Simulate shutdown by saving state to disk
- shutDownTime = timeBase2 + 50 * NS_PER_SEC;
- processor2->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_TRUE(metricProducer2_1->isActive());
- EXPECT_TRUE(metricProducer2_2->isActive());
- ttl1 -= shutDownTime - timeBase2;
- int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC -
- (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
-
- // Simulate device restarted state by creating new instance of StatsLogProcessor with the
- // same config.
- long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
- sp<StatsLogProcessor> processor3 =
- CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
-
- // Metric 1 is not active.
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor3->mMetricsManagers.size());
- it = processor3->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor3->mMetricsManagers.end());
- auto& metricsManager3 = it->second;
- EXPECT_TRUE(metricsManager3->isActive());
-
- ASSERT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
- // We assume that the index of a MetricProducer within the mAllMetricProducers
- // array follows the order in which metrics are added to the config.
- auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
- EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
- EXPECT_FALSE(metricProducer3_1->isActive());
-
- auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
- EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
- EXPECT_TRUE(metricProducer3_2->isActive());
-
- ASSERT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
- // The key in mEventActivationMap is the index of the associated atom matcher. We assume
- // that matchers are indexed in the order that they are added to the config.
- const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
- EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
- EXPECT_EQ(0, activation3_1_1->start_ns);
- EXPECT_EQ(kNotActive, activation3_1_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
-
- const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
- EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
- EXPECT_EQ(0, activation3_1_2->start_ns);
- EXPECT_EQ(kNotActive, activation3_1_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
- // }}}----------------------------------------------------------------------------------
-
- // Load saved state from disk.
- processor3->LoadActiveConfigsFromDisk();
-
- // Metric 1 active: Activation 1 is active, Activation 2 is active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer3_1->isActive());
- EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
- EXPECT_EQ(kActive, activation3_1_1->state);
- EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
- EXPECT_EQ(kActive, activation3_1_2->state);
-
- EXPECT_TRUE(metricProducer3_2->isActive());
- // }}}-------------------------------------------------------------------------------
-
- // Trigger Activation 2 for Metric 1 again.
- screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
- android::view::DISPLAY_STATE_ON);
- processor3->OnLogEvent(screenOnEvent.get());
-
- // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
- // Activation 2 is set to active
- // Metric 2 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer3_1->isActive());
- EXPECT_EQ(kNotActive, activation3_1_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
- EXPECT_EQ(kActive, activation3_1_2->state);
-
- EXPECT_TRUE(metricProducer3_2->isActive());
- // }}}---------------------------------------------------------------------------
-}
-
-TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
- int uid = 9876;
- long configId = 12341;
-
- // Create config with 3 metrics:
- // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
- // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
- // Metric 3: Always active
- StatsdConfig config1;
- config1.set_id(configId);
- config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
- auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
- *config1.add_atom_matcher() = wakelockAcquireMatcher;
- *config1.add_atom_matcher() = screenOnMatcher;
- *config1.add_atom_matcher() = jobStartMatcher;
- *config1.add_atom_matcher() = jobFinishMatcher;
-
- long metricId1 = 1234561;
- long metricId2 = 1234562;
- long metricId3 = 1234563;
-
- auto countMetric1 = config1.add_count_metric();
- countMetric1->set_id(metricId1);
- countMetric1->set_what(wakelockAcquireMatcher.id());
- countMetric1->set_bucket(FIVE_MINUTES);
-
- auto countMetric2 = config1.add_count_metric();
- countMetric2->set_id(metricId2);
- countMetric2->set_what(wakelockAcquireMatcher.id());
- countMetric2->set_bucket(FIVE_MINUTES);
-
- auto countMetric3 = config1.add_count_metric();
- countMetric3->set_id(metricId3);
- countMetric3->set_what(wakelockAcquireMatcher.id());
- countMetric3->set_bucket(FIVE_MINUTES);
-
- // Metric 1 activates on boot for wakelock acquire, immediately for screen on.
- auto metric1Activation = config1.add_metric_activation();
- metric1Activation->set_metric_id(metricId1);
- auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
- metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
- metric1ActivationTrigger1->set_ttl_seconds(100);
- metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
- auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
- metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
- metric1ActivationTrigger2->set_ttl_seconds(200);
- metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-
- // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
- auto metric2Activation = config1.add_metric_activation();
- metric2Activation->set_metric_id(metricId2);
- auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
- metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
- metric2ActivationTrigger1->set_ttl_seconds(100);
- metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
- auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
- metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
- metric2ActivationTrigger2->set_ttl_seconds(200);
- metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-
- // Send the config.
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- string serialized = config1.SerializeAsString();
- service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
-
- // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
- StatsdConfig tmpConfig;
- ConfigKey cfgKey1(uid, configId);
- EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
-
- // Metric 1 is not active.
- // Metric 2 is not active.
- // Metric 3 is active.
- // {{{---------------------------------------------------------------------------
- sp<StatsLogProcessor> processor = service->mProcessor;
- ASSERT_EQ(1, processor->mMetricsManagers.size());
- auto it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
- auto& metricsManager1 = it->second;
- EXPECT_TRUE(metricsManager1->isActive());
- ASSERT_EQ(3, metricsManager1->mAllMetricProducers.size());
-
- auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
- EXPECT_EQ(metricId1, metricProducer1->getMetricId());
- EXPECT_FALSE(metricProducer1->isActive());
-
- auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
- EXPECT_EQ(metricId2, metricProducer2->getMetricId());
- EXPECT_FALSE(metricProducer2->isActive());
-
- auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
- EXPECT_EQ(metricId3, metricProducer3->getMetricId());
- EXPECT_TRUE(metricProducer3->isActive());
-
- // Check event activations.
- ASSERT_EQ(metricsManager1->mAllAtomMatchingTrackers.size(), 4);
- EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[0]->getId(),
- metric1ActivationTrigger1->atom_matcher_id());
- const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
- EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kNotActive, activation1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
-
- EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[1]->getId(),
- metric1ActivationTrigger2->atom_matcher_id());
- const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
- EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
-
- EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[2]->getId(),
- metric2ActivationTrigger1->atom_matcher_id());
- const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
- EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
- EXPECT_EQ(0, activation3->start_ns);
- EXPECT_EQ(kNotActive, activation3->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
-
- EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[3]->getId(),
- metric2ActivationTrigger2->atom_matcher_id());
- const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
- EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
- EXPECT_EQ(0, activation4->start_ns);
- EXPECT_EQ(kNotActive, activation4->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
- // }}}------------------------------------------------------------------------------
-
- // Trigger Activation 1 for Metric 1. Should activate on boot.
- // Trigger Activation 4 for Metric 2. Should activate immediately.
- int64_t configAddedTimeNs = metricsManager1->mLastReportTimeNs;
- std::vector<int> attributionUids = {111};
- std::vector<string> attributionTags = {"App1"};
- std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(
- 1 + configAddedTimeNs, attributionUids, attributionTags, "wl1");
- processor->OnLogEvent(event1.get());
-
- std::unique_ptr<LogEvent> event2 = CreateFinishScheduledJobEvent(
- 2 + configAddedTimeNs, attributionUids, attributionTags, "finish1");
- processor->OnLogEvent(event2.get());
-
- // Metric 1 is not active; Activation 1 set to kActiveOnBoot
- // Metric 2 is active. Activation 4 set to kActive
- // Metric 3 is active.
- // {{{---------------------------------------------------------------------------
- EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1->state);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
-
- EXPECT_TRUE(metricProducer2->isActive());
- EXPECT_EQ(0, activation3->start_ns);
- EXPECT_EQ(kNotActive, activation3->state);
- EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
- EXPECT_EQ(kActive, activation4->state);
-
- EXPECT_TRUE(metricProducer3->isActive());
- // }}}-----------------------------------------------------------------------------
-
- // Can't fake time with StatsService.
- // Lets get a time close to the system server death time and make sure it's sane.
- int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
- EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
- EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
-
- // System server dies.
- service->statsCompanionServiceDiedImpl();
-
- // We should have a new metrics manager. Lets get it and ensure activation status is restored.
- // {{{---------------------------------------------------------------------------
- ASSERT_EQ(1, processor->mMetricsManagers.size());
- it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
- auto& metricsManager2 = it->second;
- EXPECT_TRUE(metricsManager2->isActive());
- ASSERT_EQ(3, metricsManager2->mAllMetricProducers.size());
-
- auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
- EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
- EXPECT_FALSE(metricProducer1001->isActive());
-
- auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
- EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
- EXPECT_TRUE(metricProducer1002->isActive());
-
- auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
- EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
- EXPECT_TRUE(metricProducer1003->isActive());
-
- // Check event activations.
- // Activation 1 is kActiveOnBoot.
- // Activation 2 and 3 are not active.
- // Activation 4 is active.
- ASSERT_EQ(metricsManager2->mAllAtomMatchingTrackers.size(), 4);
- EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[0]->getId(),
- metric1ActivationTrigger1->atom_matcher_id());
- const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
- EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
- EXPECT_EQ(0, activation1001->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1001->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
-
- EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[1]->getId(),
- metric1ActivationTrigger2->atom_matcher_id());
- const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
- EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
- EXPECT_EQ(0, activation1002->start_ns);
- EXPECT_EQ(kNotActive, activation1002->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
-
- EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[2]->getId(),
- metric2ActivationTrigger1->atom_matcher_id());
- const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
- EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
- EXPECT_EQ(0, activation1003->start_ns);
- EXPECT_EQ(kNotActive, activation1003->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
-
- EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[3]->getId(),
- metric2ActivationTrigger2->atom_matcher_id());
- const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
- EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
- EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
- EXPECT_EQ(kActive, activation1004->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
- // }}}------------------------------------------------------------------------------
-
- // Clear the data stored on disk as a result of the system server death.
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST,
- &buffer);
-}
-
-TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUid) {
- int hostUid = 20;
- int isolatedUid = 30;
- uint64_t eventTimeNs = 12355;
- int atomId = 89;
- int field1 = 90;
- int field2 = 28;
- sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
- ConfigKey cfgKey;
- StatsdConfig config = MakeConfig(false);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
-
- shared_ptr<LogEvent> logEvent = makeUidLogEvent(atomId, eventTimeNs, hostUid, field1, field2);
-
- processor->OnLogEvent(logEvent.get());
-
- const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value);
-}
-
-TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUid) {
- int hostUid = 20;
- int isolatedUid = 30;
- uint64_t eventTimeNs = 12355;
- int atomId = 89;
- int field1 = 90;
- int field2 = 28;
- sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
- ConfigKey cfgKey;
- StatsdConfig config = MakeConfig(false);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
-
- shared_ptr<LogEvent> logEvent =
- makeUidLogEvent(atomId, eventTimeNs, isolatedUid, field1, field2);
-
- processor->OnLogEvent(logEvent.get());
-
- const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value);
-}
-
-TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUidAttributionChain) {
- int hostUid = 20;
- int isolatedUid = 30;
- uint64_t eventTimeNs = 12355;
- int atomId = 89;
- int field1 = 90;
- int field2 = 28;
- sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
- ConfigKey cfgKey;
- StatsdConfig config = MakeConfig(false);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
-
- shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {hostUid, 200},
- {"tag1", "tag2"}, field1, field2);
-
- processor->OnLogEvent(logEvent.get());
-
- const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
-}
-
-TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionChain) {
- int hostUid = 20;
- int isolatedUid = 30;
- uint64_t eventTimeNs = 12355;
- int atomId = 89;
- int field1 = 90;
- int field2 = 28;
- sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
- ConfigKey cfgKey;
- StatsdConfig config = MakeConfig(false);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
-
- shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {isolatedUid, 200},
- {"tag1", "tag2"}, field1, field2);
-
- processor->OnLogEvent(logEvent.get());
-
- const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
-}
-
-TEST(StatsLogProcessorTest, TestDumpReportWithoutErasingDataDoesNotUpdateTimestamp) {
- int hostUid = 20;
- int isolatedUid = 30;
- sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
- ConfigKey key(3, 4);
-
- // TODO: All tests should not persist state on disk. This removes any reports that were present.
- ProtoOutputStream proto;
- StorageManager::appendConfigMetricsReport(key, &proto, /*erase data=*/true, /*isAdb=*/false);
-
- StatsdConfig config = MakeConfig(false);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap);
- vector<uint8_t> bytes;
-
- int64_t dumpTime1Ns = 1 * NS_PER_SEC;
- processor->onDumpReport(key, dumpTime1Ns, false /* include_current_bucket */,
- true /* erase_data */, ADB_DUMP, FAST, &bytes);
-
- ConfigMetricsReportList output;
- output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_EQ(output.reports_size(), 1);
- EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime1Ns);
-
- int64_t dumpTime2Ns = 5 * NS_PER_SEC;
- processor->onDumpReport(key, dumpTime2Ns, false /* include_current_bucket */,
- false /* erase_data */, ADB_DUMP, FAST, &bytes);
-
- // Check that the dump report without clearing data is successful.
- output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_EQ(output.reports_size(), 1);
- EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime2Ns);
- EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
-
- int64_t dumpTime3Ns = 10 * NS_PER_SEC;
- processor->onDumpReport(key, dumpTime3Ns, false /* include_current_bucket */,
- true /* erase_data */, ADB_DUMP, FAST, &bytes);
-
- // Check that the previous dump report that didn't clear data did not overwrite the first dump's
- // timestamps.
- output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_EQ(output.reports_size(), 1);
- EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime3Ns);
- EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
-
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
deleted file mode 100644
index cc38c4a4067a..000000000000
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ /dev/null
@@ -1,104 +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.
-
-#include "StatsService.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-#include <android/binder_interface_utils.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <stdio.h>
-
-using namespace android;
-using namespace testing;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoOutputStream;
-using ::ndk::SharedRefBase;
-
-#ifdef __ANDROID__
-
-TEST(StatsServiceTest, TestAddConfig_simple) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- StatsdConfig config;
- config.set_id(12345);
- string serialized = config.SerializeAsString();
-
- EXPECT_TRUE(
- service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
-}
-
-TEST(StatsServiceTest, TestAddConfig_empty) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- string serialized = "";
-
- EXPECT_TRUE(
- service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
-}
-
-TEST(StatsServiceTest, TestAddConfig_invalid) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- string serialized = "Invalid config!";
-
- EXPECT_FALSE(
- service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
-}
-
-TEST(StatsServiceTest, TestGetUidFromArgs) {
- Vector<String8> args;
- args.push(String8("-1"));
- args.push(String8("0"));
- args.push(String8("1"));
- args.push(String8("a1"));
- args.push(String8(""));
-
- int32_t uid;
-
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- service->mEngBuild = true;
-
- // "-1"
- EXPECT_FALSE(service->getUidFromArgs(args, 0, uid));
-
- // "0"
- EXPECT_TRUE(service->getUidFromArgs(args, 1, uid));
- EXPECT_EQ(0, uid);
-
- // "1"
- EXPECT_TRUE(service->getUidFromArgs(args, 2, uid));
- EXPECT_EQ(1, uid);
-
- // "a1"
- EXPECT_FALSE(service->getUidFromArgs(args, 3, uid));
-
- // ""
- EXPECT_FALSE(service->getUidFromArgs(args, 4, uid));
-
- // For a non-userdebug, uid "1" cannot be impersonated.
- service->mEngBuild = false;
- EXPECT_FALSE(service->getUidFromArgs(args, 2, uid));
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
deleted file mode 100644
index 33bdc64333e0..000000000000
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ /dev/null
@@ -1,426 +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.
-
-#include "packages/UidMap.h"
-#include "StatsLogProcessor.h"
-#include "config/ConfigKey.h"
-#include "guardrail/StatsdStats.h"
-#include "logd/LogEvent.h"
-#include "hash.h"
-#include "statslog_statsdtest.h"
-#include "statsd_test_util.h"
-
-#include <android/util/ProtoOutputStream.h>
-#include <gtest/gtest.h>
-
-#include <stdio.h>
-
-using namespace android;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoOutputStream;
-using android::util::ProtoReader;
-
-#ifdef __ANDROID__
-const string kApp1 = "app1.sharing.1";
-const string kApp2 = "app2.sharing.1";
-
-TEST(UidMapTest, TestIsolatedUID) {
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- // Construct the processor with a no-op sendBroadcast function that does nothing.
- StatsLogProcessor p(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) { return true; });
-
- std::unique_ptr<LogEvent> addEvent = CreateIsolatedUidChangedEvent(
- 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/);
- EXPECT_EQ(101, m->getHostUidOrSelf(101));
- p.OnLogEvent(addEvent.get());
- EXPECT_EQ(100, m->getHostUidOrSelf(101));
-
- std::unique_ptr<LogEvent> removeEvent = CreateIsolatedUidChangedEvent(
- 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 0 /*is_create*/);
- p.OnLogEvent(removeEvent.get());
- EXPECT_EQ(101, m->getHostUidOrSelf(101));
-}
-
-TEST(UidMapTest, TestMatching) {
- UidMap m;
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> apps;
- vector<String16> versionStrings;
- vector<String16> installers;
-
- uids.push_back(1000);
- uids.push_back(1000);
- versionStrings.push_back(String16("v1"));
- versionStrings.push_back(String16("v1"));
- installers.push_back(String16(""));
- installers.push_back(String16(""));
- apps.push_back(String16(kApp1.c_str()));
- apps.push_back(String16(kApp2.c_str()));
- versions.push_back(4);
- versions.push_back(5);
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
- EXPECT_TRUE(m.hasApp(1000, kApp1));
- EXPECT_TRUE(m.hasApp(1000, kApp2));
- EXPECT_FALSE(m.hasApp(1000, "not.app"));
-
- std::set<string> name_set = m.getAppNamesFromUid(1000u, true /* returnNormalized */);
- ASSERT_EQ(name_set.size(), 2u);
- EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
- EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
-
- name_set = m.getAppNamesFromUid(12345, true /* returnNormalized */);
- EXPECT_TRUE(name_set.empty());
-}
-
-TEST(UidMapTest, TestAddAndRemove) {
- UidMap m;
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> apps;
- vector<String16> versionStrings;
- vector<String16> installers;
-
- uids.push_back(1000);
- uids.push_back(1000);
- versionStrings.push_back(String16("v1"));
- versionStrings.push_back(String16("v1"));
- installers.push_back(String16(""));
- installers.push_back(String16(""));
- apps.push_back(String16(kApp1.c_str()));
- apps.push_back(String16(kApp2.c_str()));
- versions.push_back(4);
- versions.push_back(5);
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
-
- std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- ASSERT_EQ(name_set.size(), 2u);
- EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
- EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
-
- // Update the app1 version.
- m.updateApp(2, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
- EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
-
- name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- ASSERT_EQ(name_set.size(), 2u);
- EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
- EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
-
- m.removeApp(3, String16(kApp1.c_str()), 1000);
- EXPECT_FALSE(m.hasApp(1000, kApp1));
- EXPECT_TRUE(m.hasApp(1000, kApp2));
- name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- ASSERT_EQ(name_set.size(), 1u);
- EXPECT_TRUE(name_set.find(kApp1) == name_set.end());
- EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
-
- // Remove app2.
- m.removeApp(4, String16(kApp2.c_str()), 1000);
- EXPECT_FALSE(m.hasApp(1000, kApp1));
- EXPECT_FALSE(m.hasApp(1000, kApp2));
- name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- EXPECT_TRUE(name_set.empty());
-}
-
-TEST(UidMapTest, TestUpdateApp) {
- UidMap m;
- m.updateMap(1, {1000, 1000}, {4, 5}, {String16("v4"), String16("v5")},
- {String16(kApp1.c_str()), String16(kApp2.c_str())}, {String16(""), String16("")});
- std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- ASSERT_EQ(name_set.size(), 2u);
- EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
- EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
-
- // Adds a new name for uid 1000.
- m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40, String16("v40"), String16(""));
- name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- ASSERT_EQ(name_set.size(), 3u);
- EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
- EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
- EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
- EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
-
- // This name is also reused by another uid 2000.
- m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1, String16("v1"), String16(""));
- name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */);
- ASSERT_EQ(name_set.size(), 1u);
- EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
- EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
-}
-
-static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping* results) {
- vector<uint8_t> bytes;
- bytes.resize(proto->size());
- size_t pos = 0;
- sp<ProtoReader> reader = proto->data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
- results->ParseFromArray(bytes.data(), bytes.size());
-}
-
-// Test that uid map returns at least one snapshot even if we already obtained
-// this snapshot from a previous call to getData.
-TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot) {
- UidMap m;
- // Initialize single config key.
- ConfigKey config1(1, StringToId("config1"));
- m.OnConfigUpdated(config1);
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> apps;
- vector<String16> versionStrings;
- vector<String16> installers;
- uids.push_back(1000);
- apps.push_back(String16(kApp2.c_str()));
- versionStrings.push_back(String16("v1"));
- installers.push_back(String16(""));
- versions.push_back(5);
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
-
- // Set the last timestamp for this config key to be newer.
- m.mLastUpdatePerConfigKey[config1] = 2;
-
- ProtoOutputStream proto;
- m.appendUidMap(3, config1, nullptr, true, true, &proto);
-
- // Check there's still a uidmap attached this one.
- UidMapping results;
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(1, results.snapshots_size());
- EXPECT_EQ("v1", results.snapshots(0).package_info(0).version_string());
-}
-
-TEST(UidMapTest, TestRemovedAppRetained) {
- UidMap m;
- // Initialize single config key.
- ConfigKey config1(1, StringToId("config1"));
- m.OnConfigUpdated(config1);
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> versionStrings;
- vector<String16> installers;
- vector<String16> apps;
- uids.push_back(1000);
- apps.push_back(String16(kApp2.c_str()));
- versions.push_back(5);
- versionStrings.push_back(String16("v5"));
- installers.push_back(String16(""));
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
- m.removeApp(2, String16(kApp2.c_str()), 1000);
-
- ProtoOutputStream proto;
- m.appendUidMap(3, config1, nullptr, true, true, &proto);
-
- // Snapshot should still contain this item as deleted.
- UidMapping results;
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(1, results.snapshots(0).package_info_size());
- EXPECT_EQ(true, results.snapshots(0).package_info(0).deleted());
-}
-
-TEST(UidMapTest, TestRemovedAppOverGuardrail) {
- UidMap m;
- // Initialize single config key.
- ConfigKey config1(1, StringToId("config1"));
- m.OnConfigUpdated(config1);
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> versionStrings;
- vector<String16> installers;
- vector<String16> apps;
- const int maxDeletedApps = StatsdStats::kMaxDeletedAppsInUidMap;
- for (int j = 0; j < maxDeletedApps + 10; j++) {
- uids.push_back(j);
- apps.push_back(String16(kApp1.c_str()));
- versions.push_back(j);
- versionStrings.push_back(String16("v"));
- installers.push_back(String16(""));
- }
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
-
- // First, verify that we have the expected number of items.
- UidMapping results;
- ProtoOutputStream proto;
- m.appendUidMap(3, config1, nullptr, true, true, &proto);
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size());
-
- // Now remove all the apps.
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
- for (int j = 0; j < maxDeletedApps + 10; j++) {
- m.removeApp(4, String16(kApp1.c_str()), j);
- }
-
- proto.clear();
- m.appendUidMap(5, config1, nullptr, true, true, &proto);
- // Snapshot drops the first nine items.
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(maxDeletedApps, results.snapshots(0).package_info_size());
-}
-
-TEST(UidMapTest, TestClearingOutput) {
- UidMap m;
-
- ConfigKey config1(1, StringToId("config1"));
- ConfigKey config2(1, StringToId("config2"));
-
- m.OnConfigUpdated(config1);
-
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> versionStrings;
- vector<String16> installers;
- vector<String16> apps;
- uids.push_back(1000);
- uids.push_back(1000);
- apps.push_back(String16(kApp1.c_str()));
- apps.push_back(String16(kApp2.c_str()));
- versions.push_back(4);
- versions.push_back(5);
- versionStrings.push_back(String16("v4"));
- versionStrings.push_back(String16("v5"));
- installers.push_back(String16(""));
- installers.push_back(String16(""));
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
-
- ProtoOutputStream proto;
- m.appendUidMap(2, config1, nullptr, true, true, &proto);
- UidMapping results;
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(1, results.snapshots_size());
-
- // We have to keep at least one snapshot in memory at all times.
- proto.clear();
- m.appendUidMap(2, config1, nullptr, true, true, &proto);
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(1, results.snapshots_size());
-
- // Now add another configuration.
- m.OnConfigUpdated(config2);
- m.updateApp(5, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
- ASSERT_EQ(1U, m.mChanges.size());
- proto.clear();
- m.appendUidMap(6, config1, nullptr, true, true, &proto);
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(1, results.snapshots_size());
- ASSERT_EQ(1, results.changes_size());
- ASSERT_EQ(1U, m.mChanges.size());
-
- // Add another delta update.
- m.updateApp(7, String16(kApp2.c_str()), 1001, 41, String16("v41"), String16(""));
- ASSERT_EQ(2U, m.mChanges.size());
-
- // We still can't remove anything.
- proto.clear();
- m.appendUidMap(8, config1, nullptr, true, true, &proto);
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(1, results.snapshots_size());
- ASSERT_EQ(1, results.changes_size());
- ASSERT_EQ(2U, m.mChanges.size());
-
- proto.clear();
- m.appendUidMap(9, config2, nullptr, true, true, &proto);
- protoOutputStreamToUidMapping(&proto, &results);
- ASSERT_EQ(1, results.snapshots_size());
- ASSERT_EQ(2, results.changes_size());
- // At this point both should be cleared.
- ASSERT_EQ(0U, m.mChanges.size());
-}
-
-TEST(UidMapTest, TestMemoryComputed) {
- UidMap m;
-
- ConfigKey config1(1, StringToId("config1"));
- m.OnConfigUpdated(config1);
-
- size_t startBytes = m.mBytesUsed;
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> apps;
- vector<String16> versionStrings;
- vector<String16> installers;
- uids.push_back(1000);
- apps.push_back(String16(kApp1.c_str()));
- versions.push_back(1);
- versionStrings.push_back(String16("v1"));
- installers.push_back(String16(""));
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
-
- m.updateApp(3, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
-
- ProtoOutputStream proto;
- vector<uint8_t> bytes;
- m.appendUidMap(2, config1, nullptr, true, true, &proto);
- size_t prevBytes = m.mBytesUsed;
-
- m.appendUidMap(4, config1, nullptr, true, true, &proto);
- EXPECT_TRUE(m.mBytesUsed < prevBytes);
-}
-
-TEST(UidMapTest, TestMemoryGuardrail) {
- UidMap m;
- string buf;
-
- ConfigKey config1(1, StringToId("config1"));
- m.OnConfigUpdated(config1);
-
- size_t startBytes = m.mBytesUsed;
- vector<int32_t> uids;
- vector<int64_t> versions;
- vector<String16> versionStrings;
- vector<String16> installers;
- vector<String16> apps;
- for (int i = 0; i < 100; i++) {
- uids.push_back(1);
- buf = "EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY." + to_string(i);
- apps.push_back(String16(buf.c_str()));
- versions.push_back(1);
- versionStrings.push_back(String16("v1"));
- installers.push_back(String16(""));
- }
- m.updateMap(1, uids, versions, versionStrings, apps, installers);
-
- m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2,
- String16("v2"), String16(""));
- ASSERT_EQ(1U, m.mChanges.size());
-
- // Now force deletion by limiting the memory to hold one delta change.
- m.maxBytesOverride = 120; // Since the app string alone requires >45 characters.
- m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4,
- String16("v4"), String16(""));
- ASSERT_EQ(1U, m.mChanges.size());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
deleted file mode 100644
index 64ea219c8465..000000000000
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/anomaly/AlarmTracker.h"
-
-#include <gtest/gtest.h>
-#include <log/log_time.h>
-#include <stdio.h>
-#include <vector>
-
-using namespace testing;
-using android::sp;
-using std::set;
-using std::shared_ptr;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const ConfigKey kConfigKey(0, 12345);
-
-TEST(AlarmTrackerTest, TestTriggerTimestamp) {
- sp<AlarmMonitor> subscriberAlarmMonitor =
- new AlarmMonitor(100,
- [](const shared_ptr<IStatsCompanionService>&, int64_t){},
- [](const shared_ptr<IStatsCompanionService>&){});
- Alarm alarm;
- alarm.set_offset_millis(15 * MS_PER_SEC);
- alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr
- int64_t startMillis = 100000000 * MS_PER_SEC;
- int64_t nextAlarmTime = startMillis / MS_PER_SEC + 15;
- AlarmTracker tracker(startMillis, startMillis, alarm, kConfigKey, subscriberAlarmMonitor);
-
- EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
-
- uint64_t currentTimeSec = startMillis / MS_PER_SEC + 10;
- std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarmSet =
- subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
- EXPECT_TRUE(firedAlarmSet.empty());
- tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
- EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
- EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
-
- currentTimeSec = startMillis / MS_PER_SEC + 7000;
- nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
- firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
- ASSERT_EQ(firedAlarmSet.size(), 1u);
- tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
- EXPECT_TRUE(firedAlarmSet.empty());
- EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
- EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
-
- // Alarm fires exactly on time.
- currentTimeSec = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
- nextAlarmTime = startMillis / MS_PER_SEC + 15 + 3 * 60 * 60;
- firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
- ASSERT_EQ(firedAlarmSet.size(), 1u);
- tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
- EXPECT_TRUE(firedAlarmSet.empty());
- EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
- EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
-
- // Alarm fires exactly 1 period late.
- currentTimeSec = startMillis / MS_PER_SEC + 15 + 4 * 60 * 60;
- nextAlarmTime = startMillis / MS_PER_SEC + 15 + 5 * 60 * 60;
- firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
- ASSERT_EQ(firedAlarmSet.size(), 1u);
- tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
- EXPECT_TRUE(firedAlarmSet.empty());
- EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
- EXPECT_EQ(tracker.getAlarmTimestampSec(), nextAlarmTime);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
deleted file mode 100644
index 0cc8af16c782..000000000000
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ /dev/null
@@ -1,408 +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.
-
-#include "src/anomaly/AnomalyTracker.h"
-
-#include <gtest/gtest.h>
-#include <math.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "tests/statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const ConfigKey kConfigKey(0, 12345);
-
-MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
- int pos[] = {key, 0, 0};
- HashableDimensionKey dim;
- dim.addValue(FieldValue(Field(1, pos, 0), Value(value)));
- return MetricDimensionKey(dim, DEFAULT_DIMENSION_KEY);
-}
-
-void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
- std::shared_ptr<DimToValMap> bucket) {
- for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
- (*bucket)[itr->first] += itr->second;
- }
-}
-
-std::shared_ptr<DimToValMap> MockBucket(
- const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
- std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
- AddValueToBucket(key_value_pair_list, bucket);
- return bucket;
-}
-
-// Returns the value, for the given key, in that bucket, or 0 if not present.
-int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
- const MetricDimensionKey& key) {
- const auto& itr = bucket->find(key);
- if (itr != bucket->end()) {
- return itr->second;
- }
- return 0;
-}
-
-// Returns true if keys in trueList are detected as anomalies and keys in falseList are not.
-bool detectAnomaliesPass(AnomalyTracker& tracker,
- const int64_t& bucketNum,
- const std::shared_ptr<DimToValMap>& currentBucket,
- const std::set<const MetricDimensionKey>& trueList,
- const std::set<const MetricDimensionKey>& falseList) {
- for (const MetricDimensionKey& key : trueList) {
- if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
- return false;
- }
- }
- for (const MetricDimensionKey& key : falseList) {
- if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
- return false;
- }
- }
- return true;
-}
-
-// Calls tracker.detectAndDeclareAnomaly on each key in the bucket.
-void detectAndDeclareAnomalies(AnomalyTracker& tracker,
- const int64_t& bucketNum,
- const std::shared_ptr<DimToValMap>& bucket,
- const int64_t& eventTimestamp) {
- for (const auto& kv : *bucket) {
- tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
- kv.second);
- }
-}
-
-// Asserts that the refractory time for each key in timestamps is the corresponding
-// timestamp (in ns) + refractoryPeriodSec.
-// If a timestamp value is negative, instead asserts that the refractory period is inapplicable
-// (either non-existant or already past).
-void checkRefractoryTimes(AnomalyTracker& tracker,
- const int64_t& currTimestampNs,
- const int32_t& refractoryPeriodSec,
- const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
- for (const auto& kv : timestamps) {
- if (kv.second < 0) {
- // Make sure that, if there is a refractory period, it is already past.
- EXPECT_LT(tracker.getRefractoryPeriodEndsSec(kv.first) * NS_PER_SEC,
- (uint64_t)currTimestampNs)
- << "Failure was at currTimestampNs " << currTimestampNs;
- } else {
- EXPECT_EQ(tracker.getRefractoryPeriodEndsSec(kv.first),
- std::ceil(1.0 * kv.second / NS_PER_SEC) + refractoryPeriodSec)
- << "Failure was at currTimestampNs " << currTimestampNs;
- }
- }
-}
-
-TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
- const int64_t bucketSizeNs = 30 * NS_PER_SEC;
- const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
- Alert alert;
- alert.set_num_buckets(3);
- alert.set_refractory_period_secs(refractoryPeriodSec);
- alert.set_trigger_if_sum_gt(2);
-
- AnomalyTracker anomalyTracker(alert, kConfigKey);
- MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
- MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
- MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
-
- int64_t eventTimestamp0 = 10 * NS_PER_SEC;
- int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
- int64_t eventTimestamp2 = 2 * bucketSizeNs + 12 * NS_PER_SEC;
- int64_t eventTimestamp3 = 3 * bucketSizeNs + 13 * NS_PER_SEC;
- int64_t eventTimestamp4 = 4 * bucketSizeNs + 14 * NS_PER_SEC;
- int64_t eventTimestamp5 = 5 * bucketSizeNs + 5 * NS_PER_SEC;
- int64_t eventTimestamp6 = 6 * bucketSizeNs + 16 * NS_PER_SEC;
-
- std::shared_ptr<DimToValMap> bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
- std::shared_ptr<DimToValMap> bucket1 = MockBucket({{keyA, 1}});
- std::shared_ptr<DimToValMap> bucket2 = MockBucket({{keyB, 1}});
- std::shared_ptr<DimToValMap> bucket3 = MockBucket({{keyA, 2}});
- std::shared_ptr<DimToValMap> bucket4 = MockBucket({{keyB, 5}});
- std::shared_ptr<DimToValMap> bucket5 = MockBucket({{keyA, 2}});
- std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}});
-
- // Start time with no events.
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
-
- // Event from bucket #0 occurs.
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 0, bucket0, {}, {keyA, keyB, keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 0, bucket0, eventTimestamp1);
- checkRefractoryTimes(anomalyTracker, eventTimestamp0, refractoryPeriodSec,
- {{keyA, -1}, {keyB, -1}, {keyC, -1}});
-
- // Adds past bucket #0
- anomalyTracker.addPastBucket(bucket0, 0);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
-
- // Event from bucket #1 occurs.
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1);
- checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
- {{keyA, -1}, {keyB, -1}, {keyC, -1}});
-
- // Adds past bucket #0 again. The sum does not change.
- anomalyTracker.addPastBucket(bucket0, 0);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 1, bucket1, {}, {keyA, keyB, keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 1, bucket1, eventTimestamp1 + 1);
- checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
- {{keyA, -1}, {keyB, -1}, {keyC, -1}});
-
- // Adds past bucket #1.
- anomalyTracker.addPastBucket(bucket1, 1);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
-
- // Event from bucket #2 occurs. New anomaly on keyB.
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2);
- checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
- {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
-
- // Adds past bucket #1 again. Nothing changes.
- anomalyTracker.addPastBucket(bucket1, 1);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- // Event from bucket #2 occurs (again).
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 2, bucket2, {keyB}, {keyA, keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 2, bucket2, eventTimestamp2 + 1);
- checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
- {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}});
-
- // Adds past bucket #2.
- anomalyTracker.addPastBucket(bucket2, 2);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
-
- // Event from bucket #3 occurs. New anomaly on keyA.
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 3, bucket3, {keyA}, {keyB, keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 3, bucket3, eventTimestamp3);
- checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
- {{keyA, eventTimestamp3}, {keyB, eventTimestamp2}, {keyC, -1}});
-
- // Adds bucket #3.
- anomalyTracker.addPastBucket(bucket3, 3L);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
-
- // Event from bucket #4 occurs. New anomaly on keyB.
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 4, bucket4, {keyB}, {keyA, keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 4, bucket4, eventTimestamp4);
- checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
- {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
-
- // Adds bucket #4.
- anomalyTracker.addPastBucket(bucket4, 4);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
-
- // Event from bucket #5 occurs. New anomaly on keyA, which is still in refractory.
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 5, bucket5, {keyA, keyB}, {keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 5, bucket5, eventTimestamp5);
- checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
- {{keyA, eventTimestamp3}, {keyB, eventTimestamp4}, {keyC, -1}});
-
- // Adds bucket #5.
- anomalyTracker.addPastBucket(bucket5, 5);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
-
- // Event from bucket #6 occurs. New anomaly on keyA, which is now out of refractory.
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 6, bucket6, {keyA, keyB}, {keyC}));
- detectAndDeclareAnomalies(anomalyTracker, 6, bucket6, eventTimestamp6);
- checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
- {{keyA, eventTimestamp6}, {keyB, eventTimestamp4}, {keyC, -1}});
-}
-
-TEST(AnomalyTrackerTest, TestSparseBuckets) {
- const int64_t bucketSizeNs = 30 * NS_PER_SEC;
- const int32_t refractoryPeriodSec = 2 * bucketSizeNs / NS_PER_SEC;
- Alert alert;
- alert.set_num_buckets(3);
- alert.set_refractory_period_secs(refractoryPeriodSec);
- alert.set_trigger_if_sum_gt(2);
-
- AnomalyTracker anomalyTracker(alert, kConfigKey);
- MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
- MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
- MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
- MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
- MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
-
- std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
- std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
- std::shared_ptr<DimToValMap> bucket18 = MockBucket({{keyB, 1}, {keyC, 1}});
- std::shared_ptr<DimToValMap> bucket20 = MockBucket({{keyB, 3}, {keyC, 1}});
- std::shared_ptr<DimToValMap> bucket25 = MockBucket({{keyD, 1}});
- std::shared_ptr<DimToValMap> bucket28 = MockBucket({{keyE, 2}});
-
- int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
- int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
- int64_t eventTimestamp3 = bucketSizeNs * 17 + 1;
- int64_t eventTimestamp4 = bucketSizeNs * 19 + 2;
- int64_t eventTimestamp5 = bucketSizeNs * 24 + 3;
- int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
-
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 9, bucket9, {}, {keyA, keyB, keyC, keyD}));
- detectAndDeclareAnomalies(anomalyTracker, 9, bucket9, eventTimestamp1);
- checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
- {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
-
- // Add past bucket #9
- anomalyTracker.addPastBucket(bucket9, 9);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 16, bucket16, {keyB}, {keyA, keyC, keyD}));
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
- detectAndDeclareAnomalies(anomalyTracker, 16, bucket16, eventTimestamp2);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
- checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
- {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
-
- // Add past bucket #16
- anomalyTracker.addPastBucket(bucket16, 16);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 18, bucket18, {keyB}, {keyA, keyC, keyD}));
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
- // Within refractory period.
- detectAndDeclareAnomalies(anomalyTracker, 18, bucket18, eventTimestamp3);
- checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
- {{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
-
- // Add past bucket #18
- anomalyTracker.addPastBucket(bucket18, 18);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4);
- checkRefractoryTimes(anomalyTracker, eventTimestamp4, refractoryPeriodSec,
- {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
-
- // Add bucket #18 again. Nothing changes.
- anomalyTracker.addPastBucket(bucket18, 18);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4 + 1);
- // Within refractory period.
- checkRefractoryTimes(anomalyTracker, eventTimestamp4 + 1, refractoryPeriodSec,
- {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
-
- // Add past bucket #20
- anomalyTracker.addPastBucket(bucket20, 20);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 25, bucket25, {}, {keyA, keyB, keyC, keyD}));
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- detectAndDeclareAnomalies(anomalyTracker, 25, bucket25, eventTimestamp5);
- checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
- {{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
-
- // Add past bucket #25
- anomalyTracker.addPastBucket(bucket25, 25);
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
- EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL);
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {},
- {keyA, keyB, keyC, keyD, keyE}));
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
- {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
-
- // Updates current bucket #28.
- (*bucket28)[keyE] = 5;
- EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {keyE},
- {keyA, keyB, keyC, keyD}));
- EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6 + 7);
- ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
- checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
- {{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}});
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
deleted file mode 100644
index 1d501fd5a87c..000000000000
--- a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
+++ /dev/null
@@ -1,167 +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.
-
-#include "condition/condition_util.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-#include <gtest/gtest.h>
-
-#include <stdio.h>
-#include <vector>
-
-using namespace android::os::statsd;
-using std::vector;
-
-#ifdef __ANDROID__
-
-TEST(ConditionTrackerTest, TestUnknownCondition) {
- LogicalOperation operation = LogicalOperation::AND;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
- children.push_back(2);
-
- vector<ConditionState> conditionResults;
- conditionResults.push_back(ConditionState::kUnknown);
- conditionResults.push_back(ConditionState::kFalse);
- conditionResults.push_back(ConditionState::kTrue);
-
- EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
- ConditionState::kUnknown);
-}
-
-TEST(ConditionTrackerTest, TestAndCondition) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::AND;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
- children.push_back(2);
-
- vector<ConditionState> conditionResults;
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kFalse);
- conditionResults.push_back(ConditionState::kTrue);
-
- EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
-
- conditionResults.clear();
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kTrue);
-
- EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
-}
-
-TEST(ConditionTrackerTest, TestOrCondition) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::OR;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
- children.push_back(2);
-
- vector<ConditionState> conditionResults;
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kFalse);
- conditionResults.push_back(ConditionState::kTrue);
-
- EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
-
- conditionResults.clear();
- conditionResults.push_back(ConditionState::kFalse);
- conditionResults.push_back(ConditionState::kFalse);
- conditionResults.push_back(ConditionState::kFalse);
-
- EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
-}
-
-TEST(ConditionTrackerTest, TestNotCondition) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::NOT;
-
- vector<int> children;
- children.push_back(0);
-
- vector<ConditionState> conditionResults;
- conditionResults.push_back(ConditionState::kTrue);
-
- EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
-
- conditionResults.clear();
- conditionResults.push_back(ConditionState::kFalse);
- EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
-
- children.clear();
- conditionResults.clear();
- EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
- ConditionState::kUnknown);
-}
-
-TEST(ConditionTrackerTest, TestNandCondition) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::NAND;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
-
- vector<ConditionState> conditionResults;
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kFalse);
-
- EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
-
- conditionResults.clear();
- conditionResults.push_back(ConditionState::kFalse);
- conditionResults.push_back(ConditionState::kFalse);
- EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
-
- conditionResults.clear();
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kTrue);
- EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
-}
-
-TEST(ConditionTrackerTest, TestNorCondition) {
- // Set up the matcher
- LogicalOperation operation = LogicalOperation::NOR;
-
- vector<int> children;
- children.push_back(0);
- children.push_back(1);
-
- vector<ConditionState> conditionResults;
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kFalse);
-
- EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
-
- conditionResults.clear();
- conditionResults.push_back(ConditionState::kFalse);
- conditionResults.push_back(ConditionState::kFalse);
- EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
-
- conditionResults.clear();
- conditionResults.push_back(ConditionState::kTrue);
- conditionResults.push_back(ConditionState::kTrue);
- EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp
deleted file mode 100644
index 46dc9a9d381f..000000000000
--- a/cmds/statsd/tests/condition/ConditionTimer_test.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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 "src/condition/ConditionTimer.h"
-
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static int64_t time_base = 10;
-static int64_t ct_start_time = 200;
-
-TEST(ConditionTimerTest, TestTimer_Inital_False) {
- ConditionTimer timer(false, time_base);
- EXPECT_EQ(false, timer.mCondition);
- EXPECT_EQ(0, timer.mTimerNs);
-
- EXPECT_EQ(0, timer.newBucketStart(ct_start_time));
- EXPECT_EQ(0, timer.mTimerNs);
-
- timer.onConditionChanged(true, ct_start_time + 5);
- EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs);
- EXPECT_EQ(true, timer.mCondition);
-
- EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100));
- EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs);
- EXPECT_EQ(true, timer.mCondition);
-}
-
-TEST(ConditionTimerTest, TestTimer_Inital_True) {
- ConditionTimer timer(true, time_base);
- EXPECT_EQ(true, timer.mCondition);
- EXPECT_EQ(0, timer.mTimerNs);
-
- EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time));
- EXPECT_EQ(true, timer.mCondition);
- EXPECT_EQ(0, timer.mTimerNs);
- EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs);
-
- timer.onConditionChanged(false, ct_start_time + 5);
- EXPECT_EQ(5, timer.mTimerNs);
-
- EXPECT_EQ(5, timer.newBucketStart(ct_start_time + 100));
- EXPECT_EQ(0, timer.mTimerNs);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
deleted file mode 100644
index 8998b5f98df5..000000000000
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ /dev/null
@@ -1,741 +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.
-
-#include "src/condition/SimpleConditionTracker.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-#include <vector>
-#include <numeric>
-
-using std::map;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-const ConfigKey kConfigKey(0, 12345);
-
-const int ATTRIBUTION_NODE_FIELD_ID = 1;
-const int ATTRIBUTION_UID_FIELD_ID = 1;
-const int TAG_ID = 1;
-const uint64_t protoHash = 0x123456789;
-
-SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
- bool outputSlicedUid, Position position) {
- SimplePredicate simplePredicate;
- simplePredicate.set_start(StringToId("WAKE_LOCK_ACQUIRE"));
- simplePredicate.set_stop(StringToId("WAKE_LOCK_RELEASE"));
- simplePredicate.set_stop_all(StringToId("RELEASE_ALL"));
- if (outputSlicedUid) {
- simplePredicate.mutable_dimensions()->set_field(TAG_ID);
- simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID);
- simplePredicate.mutable_dimensions()->mutable_child(0)->set_position(position);
- simplePredicate.mutable_dimensions()->mutable_child(0)->add_child()->set_field(
- ATTRIBUTION_UID_FIELD_ID);
- }
-
- simplePredicate.set_count_nesting(countNesting);
- simplePredicate.set_initial_value(defaultFalse ? SimplePredicate_InitialValue_FALSE
- : SimplePredicate_InitialValue_UNKNOWN);
- return simplePredicate;
-}
-
-void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp,
- const vector<int>& uids, const string& wl, int acquire) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
- vector<std::string> tags(uids.size()); // vector of empty strings
- writeAttribution(statsEvent, uids, tags);
-
- AStatsEvent_writeString(statsEvent, wl.c_str());
- AStatsEvent_writeInt32(statsEvent, acquire);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-} // anonymous namespace
-
-
-std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey(
- const Position position,
- const std::vector<int> &uids, const string& conditionName) {
- std::map<int64_t, HashableDimensionKey> outputKeyMap;
- std::vector<int> uid_indexes;
- int pos[] = {1, 1, 1};
- int depth = 2;
- Field field(1, pos, depth);
- switch(position) {
- case Position::FIRST:
- uid_indexes.push_back(0);
- break;
- case Position::LAST:
- uid_indexes.push_back(uids.size() - 1);
- field.setField(0x02018001);
- break;
- case Position::ANY:
- uid_indexes.resize(uids.size());
- std::iota(uid_indexes.begin(), uid_indexes.end(), 0);
- field.setField(0x02010001);
- break;
- default:
- break;
- }
-
- for (const int idx : uid_indexes) {
- Value value((int32_t)uids[idx]);
- HashableDimensionKey dim;
- dim.addValue(FieldValue(field, value));
- outputKeyMap[StringToId(conditionName)] = dim;
- }
- return outputKeyMap;
-}
-
-TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueFalse) {
- SimplePredicate simplePredicate;
- simplePredicate.set_start(StringToId("SCREEN_TURNED_ON"));
- simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF"));
- simplePredicate.set_count_nesting(false);
- simplePredicate.set_initial_value(SimplePredicate_InitialValue_FALSE);
-
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
-
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
- 0 /*tracker index*/, simplePredicate,
- trackerNameIndexMap);
-
- ConditionKey queryKey;
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-
- // Check that initial condition is false.
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-
- vector<MatchingState> matcherState;
- vector<bool> changedCache(1, false);
-
- // Matched stop event.
- // Check that condition is still false.
- unique_ptr<LogEvent> screenOffEvent =
- CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF);
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched
- matcherState.push_back(MatchingState::kMatched); // Off matcher matched
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- EXPECT_FALSE(changedCache[0]);
-
- // Matched start event.
- // Check that condition has changed to true.
- unique_ptr<LogEvent> screenOnEvent =
- CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON);
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched); // On matcher matched
- matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- EXPECT_TRUE(changedCache[0]);
-}
-
-TEST(SimpleConditionTrackerTest, TestNonSlicedInitialValueUnknown) {
- SimplePredicate simplePredicate;
- simplePredicate.set_start(StringToId("SCREEN_TURNED_ON"));
- simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF"));
- simplePredicate.set_count_nesting(false);
- simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN);
-
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
-
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
- 0 /*tracker index*/, simplePredicate,
- trackerNameIndexMap);
-
- ConditionKey queryKey;
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-
- // Check that initial condition is unknown.
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
- EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
-
- vector<MatchingState> matcherState;
- vector<bool> changedCache(1, false);
-
- // Matched stop event.
- // Check that condition is changed to false.
- unique_ptr<LogEvent> screenOffEvent =
- CreateScreenStateChangedEvent(/*timestamp=*/50, android::view::DISPLAY_STATE_OFF);
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched); // On matcher not matched
- matcherState.push_back(MatchingState::kMatched); // Off matcher matched
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.evaluateCondition(*screenOffEvent, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- EXPECT_TRUE(changedCache[0]);
-
- // Matched start event.
- // Check that condition has changed to true.
- unique_ptr<LogEvent> screenOnEvent =
- CreateScreenStateChangedEvent(/*timestamp=*/100, android::view::DISPLAY_STATE_ON);
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched); // On matcher matched
- matcherState.push_back(MatchingState::kNotMatched); // Off matcher not matched
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(*screenOnEvent, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- EXPECT_TRUE(changedCache[0]);
-}
-
-TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
- SimplePredicate simplePredicate;
- simplePredicate.set_start(StringToId("SCREEN_TURNED_ON"));
- simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF"));
- simplePredicate.set_count_nesting(false);
- simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN);
-
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
-
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
- 0 /*tracker index*/, simplePredicate,
- trackerNameIndexMap);
- EXPECT_FALSE(conditionTracker.isSliced());
-
- // This event is not accessed in this test besides dimensions which is why this is okay.
- // This is technically an invalid LogEvent because we do not call parseBuffer.
- LogEvent event(/*uid=*/0, /*pid=*/0);
-
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
-
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
- // not matched start or stop. condition doesn't change
- EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
- EXPECT_FALSE(changedCache[0]);
-
- // prepare a case for match start.
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
- // now condition should change to true.
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- EXPECT_TRUE(changedCache[0]);
-
- // match nothing.
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- EXPECT_FALSE(changedCache[0]);
-
- // the case for match stop.
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
-
- // condition changes to false.
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- EXPECT_TRUE(changedCache[0]);
-
- // match stop again.
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
- // condition should still be false. not changed.
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- EXPECT_FALSE(changedCache[0]);
-}
-
-TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
- std::vector<sp<ConditionTracker>> allConditions;
- SimplePredicate simplePredicate;
- simplePredicate.set_start(StringToId("SCREEN_TURNED_ON"));
- simplePredicate.set_stop(StringToId("SCREEN_TURNED_OFF"));
- simplePredicate.set_count_nesting(true);
-
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
- trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
-
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
- 0 /*condition tracker index*/, simplePredicate,
- trackerNameIndexMap);
- EXPECT_FALSE(conditionTracker.isSliced());
-
- // This event is not accessed in this test besides dimensions which is why this is okay.
- // This is technically an invalid LogEvent because we do not call parseBuffer.
- LogEvent event(/*uid=*/0, /*pid=*/0);
-
- // one matched start
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
-
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- EXPECT_TRUE(changedCache[0]);
-
- // prepare for another matched start.
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
-
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- EXPECT_FALSE(changedCache[0]);
-
- // ONE MATCHED STOP
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
- // result should still be true
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- EXPECT_FALSE(changedCache[0]);
-
- // ANOTHER MATCHED STOP
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
-
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- EXPECT_TRUE(changedCache[0]);
-}
-
-TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
- std::vector<sp<ConditionTracker>> allConditions;
- for (Position position : {Position::FIRST, Position::LAST}) {
- SimplePredicate simplePredicate = getWakeLockHeldCondition(
- true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
- position);
- string conditionName = "WL_HELD_BY_UID2";
-
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
- trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
- trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
- 0 /*condition tracker index*/, simplePredicate,
- trackerNameIndexMap);
-
- std::vector<int> uids = {111, 222, 333};
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1);
-
- // one matched start
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
-
- conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
- changedCache);
-
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
- } else {
- ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
- }
- EXPECT_TRUE(changedCache[0]);
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
- EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
- } else {
- EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(),
- uids.size());
- }
-
- // Now test query
- const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-
- // another wake lock acquired by this uid
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/1);
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_FALSE(changedCache[0]);
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
- } else {
- ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
- }
- EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
- EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-
-
- // wake lock 1 release
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0);
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
- changedCache);
- // nothing changes, because wake lock 2 is still held for this uid
- EXPECT_FALSE(changedCache[0]);
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
- } else {
- ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
- }
- EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
- EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0);
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
- changedCache);
- ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
- EXPECT_TRUE(changedCache[0]);
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
- EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
- } else {
- EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(),
- uids.size());
- }
-
- // query again
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- }
-
-}
-
-TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
- std::vector<sp<ConditionTracker>> allConditions;
-
- SimplePredicate simplePredicate =
- getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/,
- false /*slice output by uid*/, Position::ANY /* position */);
- string conditionName = "WL_HELD";
-
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
- trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
- trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
- 0 /*condition tracker index*/, simplePredicate,
- trackerNameIndexMap);
-
- EXPECT_FALSE(conditionTracker.isSliced());
-
- std::vector<int> uids1 = {111, 1111, 11111};
- string uid1_wl1 = "wl1_1";
- std::vector<int> uids2 = {222, 2222, 22222};
- string uid2_wl1 = "wl2_1";
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, /*acquire=*/1);
-
- // one matched start for uid1
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
-
- conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
- changedCache);
-
- ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
- EXPECT_TRUE(changedCache[0]);
-
- // Now test query
- ConditionKey queryKey;
- conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-
- // another wake lock acquired by this uid
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, /*acquire=*/1);
-
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_FALSE(changedCache[0]);
-
- // uid1 wake lock 1 release
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1,
- /*release=*/0); // now release it.
-
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
- changedCache);
- // nothing changes, because uid2 is still holding wl.
- EXPECT_FALSE(changedCache[0]);
-
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1,
- /*acquire=*/0); // now release it.
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
- changedCache);
- ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
- EXPECT_TRUE(changedCache[0]);
-
- // query again
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-}
-
-TEST(SimpleConditionTrackerTest, TestStopAll) {
- std::vector<sp<ConditionTracker>> allConditions;
- for (Position position : {Position::FIRST, Position::LAST}) {
- SimplePredicate simplePredicate =
- getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/,
- true /*output slice by uid*/, position);
- string conditionName = "WL_HELD_BY_UID3";
-
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
- trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
- trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-
- SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
- 0 /*condition tracker index*/, simplePredicate,
- trackerNameIndexMap);
-
- std::vector<int> uids1 = {111, 1111, 11111};
- std::vector<int> uids2 = {222, 2222, 22222};
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, "wl1", /*acquire=*/1);
-
- // one matched start
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
-
- conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
- changedCache);
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
- } else {
- ASSERT_EQ(uids1.size(), conditionTracker.mSlicedConditionState.size());
- }
- EXPECT_TRUE(changedCache[0]);
- {
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
- EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
- } else {
- EXPECT_EQ(uids1.size(),
- conditionTracker.getChangedToTrueDimensions(allConditions)->size());
- EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
- }
- }
-
- // Now test query
- const auto queryKey = getWakeLockQueryKey(position, uids1, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-
- // another wake lock acquired by uid2
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1);
-
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
- changedCache);
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
- } else {
- ASSERT_EQ(uids1.size() + uids2.size(), conditionTracker.mSlicedConditionState.size());
- }
- EXPECT_TRUE(changedCache[0]);
- {
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
- EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
- } else {
- EXPECT_EQ(uids2.size(),
- conditionTracker.getChangedToTrueDimensions(allConditions)->size());
- EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
- }
- }
-
- // TEST QUERY
- const auto queryKey2 = getWakeLockQueryKey(position, uids2, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
-
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-
- // stop all event
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1);
-
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
-
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_TRUE(changedCache[0]);
- ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
- {
- if (position == Position::FIRST || position == Position::LAST) {
- ASSERT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size());
- EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
- } else {
- EXPECT_EQ(uids1.size() + uids2.size(),
- conditionTracker.getChangedToFalseDimensions(allConditions)->size());
- EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
- }
- }
-
- // TEST QUERY
- const auto queryKey3 = getWakeLockQueryKey(position, uids1, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-
- // TEST QUERY
- const auto queryKey4 = getWakeLockQueryKey(position, uids2, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
deleted file mode 100644
index 93b278388a1b..000000000000
--- a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
+++ /dev/null
@@ -1,92 +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.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateStatsdConfig() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto alarm = config.add_alarm();
- alarm->set_id(123456);
- alarm->set_offset_millis(TimeUnitToBucketSizeInMillis(TEN_MINUTES));
- alarm->set_period_millis(TimeUnitToBucketSizeInMillis(ONE_HOUR));
-
- alarm = config.add_alarm();
- alarm->set_id(654321);
- alarm->set_offset_millis(TimeUnitToBucketSizeInMillis(FIVE_MINUTES));
- alarm->set_period_millis(TimeUnitToBucketSizeInMillis(THIRTY_MINUTES));
- return config;
-}
-
-} // namespace
-
-TEST(AlarmE2eTest, TestMultipleAlarms) {
- auto config = CreateStatsdConfig();
- int64_t bucketStartTimeNs = 10000000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(2u, processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers.size());
-
- auto alarmTracker1 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[0];
- auto alarmTracker2 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[1];
-
- int64_t alarmTimestampSec0 = bucketStartTimeNs / NS_PER_SEC + 10 * 60;
- int64_t alarmTimestampSec1 = bucketStartTimeNs / NS_PER_SEC + 5 * 60;
- EXPECT_EQ(alarmTimestampSec0, alarmTracker1->getAlarmTimestampSec());
- EXPECT_EQ(alarmTimestampSec1, alarmTracker2->getAlarmTimestampSec());
-
- // Alarm fired.
- const int64_t alarmFiredTimestampSec0 = alarmTimestampSec1 + 5;
- auto alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan(
- static_cast<uint32_t>(alarmFiredTimestampSec0));
- ASSERT_EQ(1u, alarmSet.size());
- processor->onPeriodicAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
- EXPECT_EQ(alarmTimestampSec0, alarmTracker1->getAlarmTimestampSec());
- EXPECT_EQ(alarmTimestampSec1 + 30 * 60, alarmTracker2->getAlarmTimestampSec());
-
- // Alarms fired very late.
- const int64_t alarmFiredTimestampSec1 = alarmTimestampSec0 + 2 * 60 * 60 + 125;
- alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan(
- static_cast<uint32_t>(alarmFiredTimestampSec1));
- ASSERT_EQ(2u, alarmSet.size());
- processor->onPeriodicAlarmFired(alarmFiredTimestampSec1 * NS_PER_SEC, alarmSet);
- EXPECT_EQ(alarmTimestampSec0 + 60 * 60 * 3, alarmTracker1->getAlarmTimestampSec());
- EXPECT_EQ(alarmTimestampSec1 + 30 * 60 * 5, alarmTracker2->getAlarmTimestampSec());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
deleted file mode 100644
index af9436b98ec8..000000000000
--- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
+++ /dev/null
@@ -1,390 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <gtest/gtest.h>
-
-#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-
- *config.add_atom_matcher() = wakelockAcquireMatcher;
-
- auto countMetric = config.add_count_metric();
- countMetric->set_id(123456);
- countMetric->set_what(wakelockAcquireMatcher.id());
- *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- countMetric->set_bucket(FIVE_MINUTES);
-
- auto alert = config.add_alert();
- alert->set_id(StringToId("alert"));
- alert->set_metric_id(123456);
- alert->set_num_buckets(num_buckets);
- alert->set_refractory_period_secs(refractory_period_sec);
- alert->set_trigger_if_sum_gt(threshold);
- return config;
-}
-
-} // namespace
-
-TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
- const int num_buckets = 1;
- const int threshold = 3;
- const int refractory_period_sec = 10;
- auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
- const uint64_t alert_id = config.alert(0).id();
-
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-
- std::vector<int> attributionUids1 = {111};
- std::vector<string> attributionTags1 = {"App1"};
- std::vector<int> attributionUids2 = {111, 222};
- std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
- std::vector<int> attributionUids3 = {111, 333};
- std::vector<string> attributionTags3 = {"App1", "App3"};
- std::vector<int> attributionUids4 = {222, 333};
- std::vector<string> attributionTags4 = {"GMSCoreModule1", "App3"};
- std::vector<int> attributionUids5 = {222};
- std::vector<string> attributionTags5 = {"GMSCoreModule1"};
-
- FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)111));
- HashableDimensionKey whatKey1({fieldValue1});
- MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-
- FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)222));
- HashableDimensionKey whatKey2({fieldValue2});
- MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4,
- "wl2");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
- "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5,
- "wl2");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3,
- "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5,
- "wl2");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- // Fired alarm and refractory period end timestamp updated.
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1,
- "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1,
- "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4,
- attributionTags4, "wl2");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5,
- attributionTags5, "wl2");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5,
- attributionTags5, "wl2");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5,
- attributionTags5, "wl2");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-}
-
-TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
- const int num_buckets = 3;
- const int threshold = 3;
- const int refractory_period_sec = 10;
- auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
- const uint64_t alert_id = config.alert(0).id();
-
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-
- std::vector<int> attributionUids1 = {111};
- std::vector<string> attributionTags1 = {"App1"};
- std::vector<int> attributionUids2 = {111, 222};
- std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
-
- FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)111));
- HashableDimensionKey whatKey1({fieldValue1});
- MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
- "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Fired alarm and refractory period end timestamp updated.
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1,
- "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-}
-
-TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) {
- const int num_buckets = 1;
- const int threshold = 0;
- const int refractory_period_sec = 86400 * 365; // 1 year
- auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
- const int64_t alert_id = config.alert(0).id();
-
- int64_t bucketStartTimeNs = 10000000000;
-
- int configUid = 2000;
- int64_t configId = 1000;
- ConfigKey cfgKey(configUid, configId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- metadata::StatsMetadataList result;
- int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
- int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
- processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
-
- ASSERT_EQ(result.stats_metadata_size(), 0);
-}
-
-TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) {
- const int num_buckets = 1;
- const int threshold = 0;
- const int refractory_period_sec = 86400 * 365; // 1 year
- auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
- const int64_t alert_id = config.alert(0).id();
-
- int64_t bucketStartTimeNs = 10000000000;
-
- int configUid = 2000;
- int64_t configId = 1000;
- ConfigKey cfgKey(configUid, configId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-
- std::vector<int> attributionUids1 = {111};
- std::vector<string> attributionTags1 = {"App1"};
- std::vector<int> attributionUids2 = {111, 222};
- std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
-
- FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)111));
- HashableDimensionKey whatKey1({fieldValue1});
- MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- metadata::StatsMetadataList result;
- int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
- int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
- processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
-
- metadata::StatsMetadata statsMetadata = result.stats_metadata(0);
- ASSERT_EQ(result.stats_metadata_size(), 1);
- EXPECT_EQ(statsMetadata.config_key().config_id(), configId);
- EXPECT_EQ(statsMetadata.config_key().uid(), configUid);
-
- metadata::AlertMetadata alertMetadata = statsMetadata.alert_metadata(0);
- ASSERT_EQ(statsMetadata.alert_metadata_size(), 1);
- EXPECT_EQ(alertMetadata.alert_id(), alert_id);
- metadata::AlertDimensionKeyedData keyedData = alertMetadata.alert_dim_keyed_data(0);
- ASSERT_EQ(alertMetadata.alert_dim_keyed_data_size(), 1);
- EXPECT_EQ(keyedData.last_refractory_ends_sec(),
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
- mockElapsedTimeNs / NS_PER_SEC +
- mockWallClockNs / NS_PER_SEC);
-
- metadata::MetricDimensionKey metadataDimKey = keyedData.dimension_key();
- metadata::FieldValue dimKeyInWhat = metadataDimKey.dimension_key_in_what(0);
- EXPECT_EQ(dimKeyInWhat.field().tag(), fieldValue1.mField.getTag());
- EXPECT_EQ(dimKeyInWhat.field().field(), fieldValue1.mField.getField());
- EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value);
-}
-
-TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk) {
- const int num_buckets = 1;
- const int threshold = 0;
- const int refractory_period_sec = 86400 * 365; // 1 year
- auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
- const int64_t alert_id = config.alert(0).id();
-
- int64_t bucketStartTimeNs = 10000000000;
-
- int configUid = 2000;
- int64_t configId = 1000;
- ConfigKey cfgKey(configUid, configId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-
- std::vector<int> attributionUids1 = {111};
- std::vector<string> attributionTags1 = {"App1"};
- std::vector<int> attributionUids2 = {111, 222};
- std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
-
- FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)111));
- HashableDimensionKey whatKey1({fieldValue1});
- MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
- int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
- processor->SaveMetadataToDisk(mockWallClockNs, mockElapsedTimeNs);
-
- auto processor2 = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- int64_t mockElapsedTimeSinceBoot = 10 * NS_PER_SEC;
- processor2->LoadMetadataFromDisk(mockWallClockNs, mockElapsedTimeSinceBoot);
-
- sp<AnomalyTracker> anomalyTracker2 =
- processor2->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
- EXPECT_EQ(anomalyTracker2->getRefractoryPeriodEndsSec(dimensionKey1) -
- mockElapsedTimeSinceBoot / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
- mockElapsedTimeNs / NS_PER_SEC);
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
deleted file mode 100644
index 70e7365ec238..000000000000
--- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
+++ /dev/null
@@ -1,527 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <android/binder_ibinder.h>
-#include <android/binder_interface_utils.h>
-#include <gtest/gtest.h>
-
-#include <vector>
-
-#include "src/StatsLogProcessor.h"
-#include "src/StatsService.h"
-#include "src/anomaly/DurationAnomalyTracker.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-using ::ndk::SharedRefBase;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-const int kConfigKey = 789130124;
-const int kCallingUid = 0;
-
-StatsdConfig CreateStatsdConfig(int num_buckets,
- uint64_t threshold_ns,
- DurationMetric::AggregationType aggregationType,
- bool nesting) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- *config.add_predicate() = screenIsOffPredicate;
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- FieldMatcher dimensions = CreateAttributionUidDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock.
- *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
- holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting);
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("WakelockDuration"));
- durationMetric->set_what(holdingWakelockPredicate.id());
- durationMetric->set_condition(screenIsOffPredicate.id());
- durationMetric->set_aggregation_type(aggregationType);
- *durationMetric->mutable_dimensions_in_what() =
- CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- durationMetric->set_bucket(FIVE_MINUTES);
-
- auto alert = config.add_alert();
- alert->set_id(StringToId("alert"));
- alert->set_metric_id(StringToId("WakelockDuration"));
- alert->set_num_buckets(num_buckets);
- alert->set_refractory_period_secs(2);
- alert->set_trigger_if_sum_gt(threshold_ns);
- return config;
-}
-
-std::vector<int> attributionUids1 = {111, 222};
-std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"};
-
-std::vector<int> attributionUids2 = {111, 222};
-std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"};
-
-std::vector<int> attributionUids3 = {222};
-std::vector<string> attributionTags3 = {"GMSCoreModule1"};
-
-MetricDimensionKey dimensionKey1(
- HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
- (int32_t)0x02010101),
- Value((int32_t)111))}),
- DEFAULT_DIMENSION_KEY);
-
-MetricDimensionKey dimensionKey2(
- HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
- (int32_t)0x02010101), Value((int32_t)222))}),
- DEFAULT_DIMENSION_KEY);
-
-void sendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) {
- string str;
- config.SerializeToString(&str);
- std::vector<uint8_t> configAsVec(str.begin(), str.end());
- service->addConfiguration(kConfigKey, configAsVec, kCallingUid);
-}
-
-} // namespace
-
-TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
- const int num_buckets = 1;
- const uint64_t threshold_ns = NS_PER_SEC;
- auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
- const uint64_t alert_id = config.alert(0).id();
- const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- sendConfig(service, config);
-
- auto processor = service->mProcessor;
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- int64_t bucketStartTimeNs = processor->mTimeBaseNs;
- int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
-
- sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-
- auto screen_on_event = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- auto screen_off_event = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screen_on_event.get());
- processor->OnLogEvent(screen_off_event.get());
-
- // Acquire wakelock wl1.
- auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
- auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Acquire wakelock wl1 within bucket #0.
- acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Release wakelock wl1. One anomaly detected.
- release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109,
- attributionUids2, attributionTags2, "wl1");
- processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Acquire wakelock wl1.
- acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
- // end of the refractory period.
- const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
- (uint32_t)alarmFiredTimestampSec0);
- EXPECT_EQ(alarmFiredTimestampSec0,
- processor->getAnomalyAlarmMonitor()->getRegisteredAlarmTimeSec());
-
- // Anomaly alarm fired.
- auto alarmTriggerEvent = CreateBatterySaverOnEvent(alarmFiredTimestampSec0 * NS_PER_SEC);
- processor->OnLogEvent(alarmTriggerEvent.get(), alarmFiredTimestampSec0 * NS_PER_SEC);
-
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Release wakelock wl1.
- release_event =
- CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- // Within refractory period. No more anomaly detected.
- EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Acquire wakelock wl1.
- acquire_event = CreateAcquireWakelockEvent(
- roundedBucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(acquire_event.get());
- const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
- EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
- (uint64_t)alarmFiredTimestampSec1);
-
- // Release wakelock wl1.
- int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
- static_cast<uint32_t>(alarmFiredTimestampSec1));
- ASSERT_EQ(0u, alarmSet.size());
-
- // Acquire wakelock wl1 near the end of bucket #0.
- acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 2,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-
- // Release the event at early bucket #1.
- release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- // Anomaly detected when stopping the alarm. The refractory period does not change.
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Condition changes to false.
- screen_on_event =
- CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- processor->OnLogEvent(screen_on_event.get());
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-
- acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30,
- attributionUids2, attributionTags2, "wl1");
- processor->OnLogEvent(acquire_event.get());
- // The condition is false. Do not start the alarm.
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Condition turns true.
- screen_off_event =
- CreateScreenStateChangedEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screen_off_event.get());
- EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-
- // Condition turns to false.
- int64_t condition_false_time = bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1;
- screen_on_event = CreateScreenStateChangedEvent(
- condition_false_time, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- processor->OnLogEvent(screen_on_event.get(), condition_false_time);
- // Condition turns to false. Cancelled the alarm.
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- // Detected one anomaly.
- EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Condition turns to true again.
- screen_off_event =
- CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screen_off_event.get());
- EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-
- release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(release_event.get());
- EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-}
-
-TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
- const int num_buckets = 3;
- const uint64_t threshold_ns = NS_PER_SEC;
- auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true);
- const uint64_t alert_id = config.alert(0).id();
- const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- sendConfig(service, config);
-
- auto processor = service->mProcessor;
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- int64_t bucketStartTimeNs = processor->mTimeBaseNs;
- int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
-
- sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-
- auto screen_off_event = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screen_off_event.get());
-
- // Acquire wakelock "wc1" in bucket #0.
- auto acquire_event =
- CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Release wakelock "wc1" in bucket #0.
- int64_t release_event_time = roundedBucketStartTimeNs + bucketSizeNs - 1;
- auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Acquire wakelock "wc1" in bucket #1.
- acquire_event =
- CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 1,
- attributionUids2, attributionTags2, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- release_event_time = roundedBucketStartTimeNs + bucketSizeNs + NS_PER_SEC + 100;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Acquire wakelock "wc2" in bucket #2.
- acquire_event =
- CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + 1,
- attributionUids3, attributionTags3, "wl2");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3,
- anomalyTracker->getAlarmTimestampSec(dimensionKey2));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- // Release wakelock "wc2" in bucket #2.
- release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3,
- attributionTags3, "wl2");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
- EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
-
- // Acquire wakelock "wc1" in bucket #2.
- acquire_event =
- CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC,
- attributionUids2, attributionTags2, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((roundedBucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 3 + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Release wakelock "wc1" in bucket #2.
- release_event_time = roundedBucketStartTimeNs + 2 * bucketSizeNs + 3.5 * NS_PER_SEC;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids2,
- attributionTags2, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(refractory_period_sec + (release_event_time) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 4,
- attributionUids3, attributionTags3, "wl2");
- processor->OnLogEvent(acquire_event.get());
- acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 6 * bucketSizeNs + 5,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ((roundedBucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 2,
- anomalyTracker->getAlarmTimestampSec(dimensionKey2));
-
- release_event_time = roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC + 2;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids3,
- attributionTags3, "wl2");
- processor->OnLogEvent(release_event.get(), release_event_time);
- release_event = CreateReleaseWakelockEvent(release_event_time + 4, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time + 4);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
- // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
- EXPECT_EQ(refractory_period_sec +
- (int64_t)(roundedBucketStartTimeNs + 6 * bucketSizeNs + NS_PER_SEC) /
- NS_PER_SEC +
- 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-}
-
-TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
- const int num_buckets = 2;
- const uint64_t threshold_ns = 3 * NS_PER_SEC;
- auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
- const uint64_t alert_id = config.alert(0).id();
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1e6;
- const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
- config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
-
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- sendConfig(service, config);
-
- auto processor = service->mProcessor;
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
-
- int64_t bucketStartTimeNs = processor->mTimeBaseNs;
- int64_t roundedBucketStartTimeNs = bucketStartTimeNs / NS_PER_SEC * NS_PER_SEC;
-
- sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
-
- auto screen_off_event = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screen_off_event.get());
-
- // Acquire wakelock "wc1" in bucket #0.
- auto acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs - 100,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Acquire the wakelock "wc1" again.
- acquire_event =
- CreateAcquireWakelockEvent(roundedBucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- // The alarm does not change.
- EXPECT_EQ((roundedBucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // Anomaly alarm fired late.
- const int64_t firedAlarmTimestampNs = roundedBucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
- auto alarmTriggerEvent = CreateBatterySaverOnEvent(firedAlarmTimestampNs);
- processor->OnLogEvent(alarmTriggerEvent.get(), firedAlarmTimestampNs);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- acquire_event = CreateAcquireWakelockEvent(roundedBucketStartTimeNs + 2 * bucketSizeNs - 100,
- attributionUids1, attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- int64_t release_event_time = bucketStartTimeNs + 2 * bucketSizeNs + 1;
- auto release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- // Within the refractory period. No anomaly.
- EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- // A new wakelock, but still within refractory period.
- acquire_event = CreateAcquireWakelockEvent(
- roundedBucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-
- release_event =
- CreateReleaseWakelockEvent(roundedBucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC,
- attributionUids1, attributionTags1, "wl1");
- // Still in the refractory period. No anomaly.
- processor->OnLogEvent(release_event.get());
- EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
-
- acquire_event = CreateAcquireWakelockEvent(
- roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 5, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-
- release_event_time = roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 4;
- release_event = CreateReleaseWakelockEvent(release_event_time, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(release_event.get(), release_event_time);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-
- acquire_event = CreateAcquireWakelockEvent(
- roundedBucketStartTimeNs + 5 * bucketSizeNs - 2 * NS_PER_SEC - 3, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((roundedBucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey1));
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
deleted file mode 100644
index 4c2caa904f6a..000000000000
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ /dev/null
@@ -1,375 +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.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <iostream>
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateStatsdConfig(const Position position) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
- auto attributionNodeMatcher =
- wakelockAcquireMatcher.mutable_simple_atom_matcher()->add_field_value_matcher();
- attributionNodeMatcher->set_field(1);
- attributionNodeMatcher->set_position(Position::ANY);
- auto uidMatcher = attributionNodeMatcher->mutable_matches_tuple()->add_field_value_matcher();
- uidMatcher->set_field(1); // uid field.
- uidMatcher->set_eq_string("com.android.gmscore");
-
- *config.add_atom_matcher() = wakelockAcquireMatcher;
-
- auto countMetric = config.add_count_metric();
- countMetric->set_id(123456);
- countMetric->set_what(wakelockAcquireMatcher.id());
- *countMetric->mutable_dimensions_in_what() =
- CreateAttributionUidAndTagDimensions(
- util::WAKELOCK_STATE_CHANGED, {position});
- countMetric->set_bucket(FIVE_MINUTES);
- return config;
-}
-
-// GMS core node is in the middle.
-std::vector<int> attributionUids1 = {111, 222, 333};
-std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "App3"};
-
-// GMS core node is the last one.
-std::vector<int> attributionUids2 = {111, 333, 222};
-std::vector<string> attributionTags2 = {"App1", "App3", "GMSCoreModule1"};
-
-// GMS core node is the first one.
-std::vector<int> attributionUids3 = {222, 333};
-std::vector<string> attributionTags3 = {"GMSCoreModule1", "App3"};
-
-// Single GMS core node.
-std::vector<int> attributionUids4 = {222};
-std::vector<string> attributionTags4 = {"GMSCoreModule1"};
-
-// GMS core has another uid.
-std::vector<int> attributionUids5 = {111, 444, 333};
-std::vector<string> attributionTags5 = {"App1", "GMSCoreModule2", "App3"};
-
-// Multiple GMS core nodes.
-std::vector<int> attributionUids6 = {444, 222};
-std::vector<string> attributionTags6 = {"GMSCoreModule2", "GMSCoreModule1"};
-
-// No GMS core nodes
-std::vector<int> attributionUids7 = {111, 333};
-std::vector<string> attributionTags7 = {"App1", "App3"};
-
-std::vector<int> attributionUids8 = {111};
-std::vector<string> attributionTags8 = {"App1"};
-
-// GMS core node with isolated uid.
-const int isolatedUid = 666;
-std::vector<int> attributionUids9 = {isolatedUid};
-std::vector<string> attributionTags9 = {"GMSCoreModule3"};
-
-std::vector<int> attributionUids10 = {isolatedUid};
-std::vector<string> attributionTags10 = {"GMSCoreModule1"};
-
-} // namespace
-
-TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) {
- auto config = CreateStatsdConfig(Position::FIRST);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- // Here it assumes that GMS core has two uids.
- processor->getUidMap()->updateMap(
- 1, {222, 444, 111, 333}, {1, 1, 2, 2},
- {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
- {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
- String16("APP3")},
- {String16(""), String16(""), String16(""), String16("")});
-
- std::vector<std::unique_ptr<LogEvent>> events;
- // Events 1~4 are in the 1st bucket.
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2,
- attributionTags2, "wl1"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
- attributionUids3, attributionTags3, "wl1"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4,
- attributionTags4, "wl1"));
-
- // Events 5~8 are in the 3rd bucket.
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
- attributionUids5, attributionTags5, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
- attributionUids6, attributionTags6, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2,
- attributionUids7, attributionTags7, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs,
- attributionUids8, attributionTags8, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1,
- attributionUids9, attributionTags9, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100,
- attributionUids9, attributionTags9, "wl2"));
- events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222,
- isolatedUid, true /*is_create*/));
- events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222,
- isolatedUid, false /*is_create*/));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
-
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(countMetrics.data_size(), 4);
-
- auto data = countMetrics.data(0);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 111, "App1");
- ASSERT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).count(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).count(), 1);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
-
- data = countMetrics.data(1);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule1");
- ASSERT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).count(), 1);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = countMetrics.data(2);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule3");
- ASSERT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + 3 * bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs);
-
- data = countMetrics.data(3);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 444,
- "GMSCoreModule2");
- ASSERT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
-}
-
-TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) {
- auto config = CreateStatsdConfig(Position::ALL);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- // Here it assumes that GMS core has two uids.
- processor->getUidMap()->updateMap(
- 1, {222, 444, 111, 333}, {1, 1, 2, 2},
- {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
- {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
- String16("APP3")},
- {String16(""), String16(""), String16(""), String16("")});
-
- std::vector<std::unique_ptr<LogEvent>> events;
- // Events 1~4 are in the 1st bucket.
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2,
- attributionTags2, "wl1"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
- attributionUids3, attributionTags3, "wl1"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4,
- attributionTags4, "wl1"));
-
- // Events 5~8 are in the 3rd bucket.
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
- attributionUids5, attributionTags5, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
- attributionUids6, attributionTags6, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2,
- attributionUids7, attributionTags7, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs,
- attributionUids8, attributionTags8, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1,
- attributionUids10, attributionTags10, "wl2"));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100,
- attributionUids10, attributionTags10, "wl2"));
- events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222,
- isolatedUid, true /*is_create*/));
- events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222,
- isolatedUid, false /*is_create*/));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
-
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(countMetrics.data_size(), 6);
-
- auto data = countMetrics.data(0);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule1");
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(1).count());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(1);
- ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
- util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule1");
- ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
- util::WAKELOCK_STATE_CHANGED, 333, "App3");
- ASSERT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
- data = countMetrics.data(2);
- ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 444);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
- util::WAKELOCK_STATE_CHANGED, 444,
- "GMSCoreModule2");
- ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
- util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule1");
- ASSERT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(3);
- ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
- util::WAKELOCK_STATE_CHANGED, 111, "App1");
- ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
- util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule1");
- ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
- util::WAKELOCK_STATE_CHANGED, 333, "App3");
- ASSERT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(4);
- ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
- util::WAKELOCK_STATE_CHANGED, 111, "App1");
- ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
- util::WAKELOCK_STATE_CHANGED, 333, "App3");
- ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
- util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule1");
- ASSERT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(5);
- ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
- util::WAKELOCK_STATE_CHANGED, 111, "App1");
- ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 444);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
- util::WAKELOCK_STATE_CHANGED, 444,
- "GMSCoreModule2");
- ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
- util::WAKELOCK_STATE_CHANGED, 333, "App3");
- ASSERT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
deleted file mode 100644
index 0bce0baa049b..000000000000
--- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-
- *config.add_atom_matcher() = wakelockAcquireMatcher;
-
- auto countMetric = config.add_count_metric();
- countMetric->set_id(123456);
- countMetric->set_what(wakelockAcquireMatcher.id());
- *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- countMetric->set_bucket(FIVE_MINUTES);
-
- auto alert = config.add_alert();
- alert->set_id(StringToId("alert"));
- alert->set_metric_id(123456);
- alert->set_num_buckets(num_buckets);
- alert->set_refractory_period_secs(10);
- alert->set_trigger_if_sum_gt(threshold);
-
- // Two hours
- config.set_ttl_in_seconds(2 * 3600);
- return config;
-}
-
-} // namespace
-
-TEST(ConfigTtlE2eTest, TestCountMetric) {
- const int num_buckets = 1;
- const int threshold = 3;
- auto config = CreateStatsdConfig(num_buckets, threshold);
- const uint64_t alert_id = config.alert(0).id();
- const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
-
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<int> attributionUids1 = {111};
- std::vector<string> attributionTags1 = {"App1"};
-
- FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)111));
- HashableDimensionKey whatKey1({fieldValue1});
- MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
-
- FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)222));
- HashableDimensionKey whatKey2({fieldValue2});
- MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids1,
- attributionTags1, "wl2");
- processor->OnLogEvent(event.get());
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * bucketSizeNs + 2, attributionUids1,
- attributionTags1, "wl1");
- processor->OnLogEvent(event.get());
-
- EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC),
- processor->mMetricsManagers.begin()->second->getTtlEndNs());
-
- // Clear the data stored on disk as a result of the ttl.
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true,
- ADB_DUMP, FAST, &buffer);
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp
deleted file mode 100644
index e01a0b63a0ca..000000000000
--- a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-// Copyright (C) 2020 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 <thread> // std::this_thread::sleep_for
-
-#include "android-base/stringprintf.h"
-#include "src/StatsLogProcessor.h"
-#include "src/storage/StorageManager.h"
-#include "tests/statsd_test_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-#define STATS_DATA_DIR "/data/misc/stats-data"
-using android::base::StringPrintf;
-
-namespace {
-
-StatsdConfig CreateSimpleConfig() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_STATSD");
- config.set_hash_strings_in_metric_report(false);
-
- *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
- // Simple count metric so the config isn't empty.
- CountMetric* countMetric1 = config.add_count_metric();
- countMetric1->set_id(StringToId("Count1"));
- countMetric1->set_what(config.atom_matcher(0).id());
- countMetric1->set_bucket(FIVE_MINUTES);
- return config;
-}
-} // namespace
-
-// Setup for parameterized tests.
-class ConfigUpdateE2eTest : public TestWithParam<bool> {};
-
-INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eTest, ConfigUpdateE2eTest, testing::Bool());
-
-TEST_P(ConfigUpdateE2eTest, TestUidMapVersionStringInstaller) {
- sp<UidMap> uidMap = new UidMap();
- vector<int32_t> uids({1000});
- vector<int64_t> versions({1});
- vector<String16> apps({String16("app1")});
- vector<String16> versionStrings({String16("v1")});
- vector<String16> installers({String16("installer1")});
- uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
-
- StatsdConfig config = CreateSimpleConfig();
- config.set_version_strings_in_metric_report(true);
- config.set_installer_in_metric_report(false);
- int64_t baseTimeNs = getElapsedRealtimeNs();
-
- ConfigKey cfgKey(0, 12345);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
-
- // Now update.
- config.set_version_strings_in_metric_report(false);
- config.set_installer_in_metric_report(true);
- processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam());
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
- EXPECT_TRUE(metricsManager->isConfigValid());
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- // First report is written to disk when the update happens.
- ASSERT_EQ(reports.reports_size(), 2);
- UidMapping uidMapping = reports.reports(1).uid_map();
- ASSERT_EQ(uidMapping.snapshots_size(), 1);
- ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
- EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string());
- EXPECT_EQ(uidMapping.snapshots(0).package_info(0).installer(), "installer1");
-}
-
-TEST_P(ConfigUpdateE2eTest, TestHashStrings) {
- sp<UidMap> uidMap = new UidMap();
- vector<int32_t> uids({1000});
- vector<int64_t> versions({1});
- vector<String16> apps({String16("app1")});
- vector<String16> versionStrings({String16("v1")});
- vector<String16> installers({String16("installer1")});
- uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
-
- StatsdConfig config = CreateSimpleConfig();
- config.set_version_strings_in_metric_report(true);
- config.set_hash_strings_in_metric_report(true);
- int64_t baseTimeNs = getElapsedRealtimeNs();
-
- ConfigKey cfgKey(0, 12345);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
-
- // Now update.
- config.set_hash_strings_in_metric_report(false);
- processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam());
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
- EXPECT_TRUE(metricsManager->isConfigValid());
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- // First report is written to disk when the update happens.
- ASSERT_EQ(reports.reports_size(), 2);
- UidMapping uidMapping = reports.reports(1).uid_map();
- ASSERT_EQ(uidMapping.snapshots_size(), 1);
- ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
- EXPECT_TRUE(uidMapping.snapshots(0).package_info(0).has_version_string());
- EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string_hash());
-}
-
-TEST_P(ConfigUpdateE2eTest, TestAnnotations) {
- StatsdConfig config = CreateSimpleConfig();
- StatsdConfig_Annotation* annotation = config.add_annotation();
- annotation->set_field_int64(11);
- annotation->set_field_int32(1);
- int64_t baseTimeNs = getElapsedRealtimeNs();
- ConfigKey cfgKey(0, 12345);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
-
- // Now update
- config.clear_annotation();
- annotation = config.add_annotation();
- annotation->set_field_int64(22);
- annotation->set_field_int32(2);
- processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam());
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- // First report is written to disk when the update happens.
- ASSERT_EQ(reports.reports_size(), 2);
- ConfigMetricsReport report = reports.reports(1);
- EXPECT_EQ(report.annotation_size(), 1);
- EXPECT_EQ(report.annotation(0).field_int64(), 22);
- EXPECT_EQ(report.annotation(0).field_int32(), 2);
-}
-
-TEST_P(ConfigUpdateE2eTest, TestPersistLocally) {
- StatsdConfig config = CreateSimpleConfig();
- config.set_persist_locally(false);
- int64_t baseTimeNs = getElapsedRealtimeNs();
- ConfigKey cfgKey(0, 12345);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- ASSERT_EQ(reports.reports_size(), 1);
- // Number of reports should still be 1 since persist_locally is false.
- reports.Clear();
- buffer.clear();
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- ASSERT_EQ(reports.reports_size(), 1);
-
- // Now update.
- config.set_persist_locally(true);
- processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam());
-
- // Should get 2: 1 in memory + 1 on disk. Both should be saved on disk.
- reports.Clear();
- buffer.clear();
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- ASSERT_EQ(reports.reports_size(), 2);
- // Should get 3, 2 on disk + 1 in memory.
- reports.Clear();
- buffer.clear();
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- ASSERT_EQ(reports.reports_size(), 3);
- string suffix = StringPrintf("%d_%lld", cfgKey.GetUid(), (long long)cfgKey.GetId());
- StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, suffix.c_str());
- string historySuffix =
- StringPrintf("%d_%lld_history", cfgKey.GetUid(), (long long)cfgKey.GetId());
- StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, historySuffix.c_str());
-}
-
-TEST_P(ConfigUpdateE2eTest, TestNoReportMetrics) {
- StatsdConfig config = CreateSimpleConfig();
- // Second simple count metric.
- CountMetric* countMetric = config.add_count_metric();
- countMetric->set_id(StringToId("Count2"));
- countMetric->set_what(config.atom_matcher(0).id());
- countMetric->set_bucket(FIVE_MINUTES);
- config.add_no_report_metric(config.count_metric(0).id());
- int64_t baseTimeNs = getElapsedRealtimeNs();
- ConfigKey cfgKey(0, 12345);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
-
- // Now update.
- config.clear_no_report_metric();
- config.add_no_report_metric(config.count_metric(1).id());
- processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam());
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- // First report is written to disk when the update happens.
- ASSERT_EQ(reports.reports_size(), 2);
- // First report (before update) has the first count metric.
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).metric_id(), config.count_metric(1).id());
- // Second report (after update) has the first count metric.
- ASSERT_EQ(reports.reports(1).metrics_size(), 1);
- EXPECT_EQ(reports.reports(1).metrics(0).metric_id(), config.count_metric(0).id());
-}
-
-TEST_P(ConfigUpdateE2eTest, TestAtomsAllowedFromAnyUid) {
- StatsdConfig config = CreateSimpleConfig();
- int64_t baseTimeNs = getElapsedRealtimeNs();
- ConfigKey cfgKey(0, 12345);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
- // Uses AID_ROOT, which isn't in allowed log sources.
- unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent(
- baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
- processor->OnLogEvent(event.get());
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, baseTimeNs + 1001, true, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- ASSERT_EQ(reports.reports_size(), 1);
- // Check the metric and make sure it has 0 count.
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_FALSE(reports.reports(0).metrics(0).has_count_metrics());
-
- // Now update. Allow plugged state to be logged from any uid, so the atom will be counted.
- config.add_whitelisted_atom_ids(util::PLUGGED_STATE_CHANGED);
- processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config, /*modularUpdate=*/GetParam());
- unique_ptr<LogEvent> event2 = CreateBatteryStateChangedEvent(
- baseTimeNs + 2000, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
- processor->OnLogEvent(event.get());
- reports.Clear();
- buffer.clear();
- processor->onDumpReport(cfgKey, baseTimeNs + 3000, true, true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- ASSERT_EQ(reports.reports_size(), 2);
- // Check the metric and make sure it has 0 count.
- ASSERT_EQ(reports.reports(1).metrics_size(), 1);
- EXPECT_TRUE(reports.reports(1).metrics(0).has_count_metrics());
- ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data_size(), 1);
- ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
- EXPECT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
-}
-
-TEST_P(ConfigUpdateE2eTest, TestConfigTtl) {
- StatsdConfig config = CreateSimpleConfig();
- config.set_ttl_in_seconds(1);
- int64_t baseTimeNs = getElapsedRealtimeNs();
- ConfigKey cfgKey(0, 12345);
- sp<StatsLogProcessor> processor =
- CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + NS_PER_SEC);
-
- config.set_ttl_in_seconds(5);
- processor->OnConfigUpdated(baseTimeNs + 2 * NS_PER_SEC, cfgKey, config,
- /*modularUpdate=*/GetParam());
- metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + 7 * NS_PER_SEC);
-
- // Clear the data stored on disk as a result of the update.
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, baseTimeNs + 3 * NS_PER_SEC, false, true, ADB_DUMP, FAST,
- &buffer);
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
deleted file mode 100644
index 04eb40080631..000000000000
--- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
+++ /dev/null
@@ -1,901 +0,0 @@
-/*
- * 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 "src/state/StateTracker.h"
-#include "tests/statsd_test_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-/**
- * Tests the initial condition and condition after the first log events for
- * count metrics with either a combination condition or simple condition.
- *
- * Metrics should be initialized with condition kUnknown (given that the
- * predicate is using the default InitialValue of UNKNOWN). The condition should
- * be updated to either kFalse or kTrue if a condition event is logged for all
- * children conditions.
- */
-TEST(CountMetricE2eTest, TestInitialConditionChanges) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
-
- auto syncStartMatcher = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = syncStartMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateBatteryStateNoneMatcher();
- *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
-
- auto screenOnPredicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = screenOnPredicate;
-
- auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate();
- *config.add_predicate() = deviceUnpluggedPredicate;
-
- auto screenOnOnBatteryPredicate = config.add_predicate();
- screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate"));
- screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate);
- addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate);
-
- // CountSyncStartWhileScreenOnOnBattery (CombinationCondition)
- CountMetric* countMetric1 = config.add_count_metric();
- countMetric1->set_id(StringToId("CountSyncStartWhileScreenOnOnBattery"));
- countMetric1->set_what(syncStartMatcher.id());
- countMetric1->set_condition(screenOnOnBatteryPredicate->id());
- countMetric1->set_bucket(FIVE_MINUTES);
-
- // CountSyncStartWhileOnBattery (SimpleCondition)
- CountMetric* countMetric2 = config.add_count_metric();
- countMetric2->set_id(StringToId("CountSyncStartWhileOnBatterySliceScreen"));
- countMetric2->set_what(syncStartMatcher.id());
- countMetric2->set_condition(deviceUnpluggedPredicate.id());
- countMetric2->set_bucket(FIVE_MINUTES);
-
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- const uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(2, metricsManager->mAllMetricProducers.size());
-
- sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0];
- sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1];
-
- EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
-
- auto screenOnEvent =
- CreateScreenStateChangedEvent(bucketStartTimeNs + 30, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
- EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
-
- auto pluggedUsbEvent = CreateBatteryStateChangedEvent(
- bucketStartTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
- processor->OnLogEvent(pluggedUsbEvent.get());
- EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition);
-
- auto pluggedNoneEvent = CreateBatteryStateChangedEvent(
- bucketStartTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
- processor->OnLogEvent(pluggedNoneEvent.get());
- EXPECT_EQ(ConditionState::kTrue, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition);
-}
-
-/**
-* Test a count metric that has one slice_by_state with no primary fields.
-*
-* Once the CountMetricProducer is initialized, it has one atom id in
-* mSlicedStateAtoms and no entries in mStateGroupMap.
-
-* One StateTracker tracks the state atom, and it has one listener which is the
-* CountMetricProducer that was initialized.
-*/
-TEST(CountMetricE2eTest, TestSlicedState) {
- // 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::FIVE_MINUTES);
- countMetric->add_slice_by_state(state.id());
-
- // Initialize StatsLogProcessor.
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- const uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // Check that CountMetricProducer was initialized correctly.
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- /*
- bucket #1 bucket #2
- | 1 2 3 4 5 6 7 8 9 10 (minutes)
- |-----------------------------|-----------------------------|--
- x x x x x x (syncStartEvents)
- | | (ScreenIsOnEvent)
- | | (ScreenIsOffEvent)
- | (ScreenDozeEvent)
- */
- // Initialize log events - first bucket.
- std::vector<int> attributionUids1 = {123};
- std::vector<string> attributionTags1 = {"App1"};
-
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 50 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:00
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 75 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 1:25
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 150 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 2:40
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 200 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:30
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 4:20
-
- // Initialize log events - second bucket.
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 350 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 6:00
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 6:50
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 450 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 7:40
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 475 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 8:05
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 500 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 8:30
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 520 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 8:50
-
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(3, countMetrics.data_size());
-
- // For each CountMetricData, check StateValue info is correct and buckets
- // have correct counts.
- auto data = countMetrics.data(0);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-
- data = countMetrics.data(1);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(2, data.bucket_info(1).count());
-
- data = countMetrics.data(2);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(1, data.bucket_info(1).count());
-}
-
-/**
- * Test a count metric that has one slice_by_state with a mapping and no
- * primary fields.
- *
- * Once the CountMetricProducer is initialized, it has one atom id in
- * mSlicedStateAtoms and has one entry per state value in mStateGroupMap.
- *
- * One StateTracker tracks the state atom, and it has one listener which is the
- * CountMetricProducer that was initialized.
- */
-TEST(CountMetricE2eTest, TestSlicedStateWithMap) {
- // 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;
-
- int64_t screenOnId = 4444;
- int64_t screenOffId = 9876;
- auto state = CreateScreenStateWithOnOffMap(screenOnId, screenOffId);
- *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::FIVE_MINUTES);
- countMetric->add_slice_by_state(state.id());
-
- // Initialize StatsLogProcessor.
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- const uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- // Check that CountMetricProducer was initialized correctly.
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1);
-
- StateMap map = state.map();
- for (auto group : map.group()) {
- for (auto value : group.value()) {
- EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
- group.group_id());
- }
- }
-
- /*
- bucket #1 bucket #2
- | 1 2 3 4 5 6 7 8 9 10 (minutes)
- |-----------------------------|-----------------------------|--
- x x x x x x x x x (syncStartEvents)
- -----------------------------------------------------------SCREEN_OFF events
- | | (ScreenStateOffEvent = 1)
- | | (ScreenStateDozeEvent = 3)
- | (ScreenStateDozeSuspendEvent =
- 4)
- -----------------------------------------------------------SCREEN_ON events
- | | (ScreenStateOnEvent = 2)
- | (ScreenStateVrEvent = 5)
- | (ScreenStateOnSuspendEvent = 6)
- */
- // Initialize log events - first bucket.
- std::vector<int> attributionUids1 = {123};
- std::vector<string> attributionTags1 = {"App1"};
-
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 20 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 0:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 30 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 1:10
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 90 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 120 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 2:10
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 150 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:40
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 180 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:10
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 3:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 210 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 4:20
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 280 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:50
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 285 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 4:55
-
- // Initialize log events - second bucket.
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 360 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 6:10
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 390 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 430 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 440 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 7:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 540 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 9:10
- events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 570 * NS_PER_SEC, attributionUids1,
- attributionTags1, "sync_name")); // 9:40
-
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(3, countMetrics.data_size());
-
- // For each CountMetricData, check StateValue info is correct and buckets
- // have correct counts.
- auto data = countMetrics.data(0);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-
- data = countMetrics.data(1);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(1, data.bucket_info(1).count());
-
- data = countMetrics.data(2);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(4, data.bucket_info(0).count());
- EXPECT_EQ(2, data.bucket_info(1).count());
-}
-
-/**
-* Test a count metric that has one slice_by_state with a primary field.
-
-* Once the CountMetricProducer is initialized, it should have one
-* MetricStateLink stored. State querying using a non-empty primary key
-* should also work as intended.
-*/
-TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto appCrashMatcher =
- CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
- *config.add_atom_matcher() = appCrashMatcher;
-
- auto state = CreateUidProcessState();
- *config.add_state() = state;
-
- // Create count metric that slices by uid process state.
- int64_t metricId = 123456;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(metricId);
- countMetric->set_what(appCrashMatcher.id());
- countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
- countMetric->add_slice_by_state(state.id());
- MetricStateLink* stateLink = countMetric->add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/});
-
- // Initialize StatsLogProcessor.
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- const uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-
- // Check that CountMetricProducer was initialized correctly.
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
- ASSERT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
-
- /*
- NOTE: "1" or "2" represents the uid associated with the state/app crash event
- bucket #1 bucket #2
- | 1 2 3 4 5 6 7 8 9 10
- |------------------------|-------------------------|--
- 1 1 1 1 1 2 1 1 2 (AppCrashEvents)
- -----------------------------------------------------PROCESS STATE events
- 1 2 (TopEvent = 1002)
- 1 1 (ForegroundServiceEvent = 1003)
- 2 (ImportantBackgroundEvent = 1006)
- 1 1 1 (ImportantForegroundEvent = 1005)
-
- Based on the diagram above, an AppCrashEvent querying for process state value would return:
- - StateTracker::kStateUnknown
- - Important foreground
- - Top
- - Important foreground
- - Foreground service
- - Top (both the app crash and state still have matching uid = 2)
-
- - Foreground service
- - Foreground service
- - Important background
- */
- // Initialize log events - first bucket.
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:40
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 210 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 3:40
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55
-
- // Initialize log events - second bucket.
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 390 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:40
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 430 * NS_PER_SEC, 2 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:20
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 440 * NS_PER_SEC, 1 /*uid*/)); // 7:30
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40
-
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(5, countMetrics.data_size());
-
- // For each CountMetricData, check StateValue info is correct and buckets
- // have correct counts.
- auto data = countMetrics.data(0);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-
- data = countMetrics.data(1);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(2, data.bucket_info(0).count());
-
- data = countMetrics.data(2);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(2, data.bucket_info(1).count());
-
- data = countMetrics.data(3);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(2, data.bucket_info(0).count());
-
- data = countMetrics.data(4);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-}
-
-TEST(CountMetricE2eTest, TestMultipleSlicedStates) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto appCrashMatcher =
- CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
- *config.add_atom_matcher() = appCrashMatcher;
-
- int64_t screenOnId = 4444;
- int64_t screenOffId = 9876;
- auto state1 = CreateScreenStateWithOnOffMap(screenOnId, screenOffId);
- *config.add_state() = state1;
- auto state2 = CreateUidProcessState();
- *config.add_state() = state2;
-
- // Create count metric that slices by screen state with on/off map and
- // slices by uid process state.
- int64_t metricId = 123456;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(metricId);
- countMetric->set_what(appCrashMatcher.id());
- countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
- countMetric->add_slice_by_state(state1.id());
- countMetric->add_slice_by_state(state2.id());
- MetricStateLink* stateLink = countMetric->add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/});
-
- // Initialize StatsLogProcessor.
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- const uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // Check that StateTrackers were properly initialized.
- EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-
- // Check that CountMetricProducer was initialized correctly.
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1);
- ASSERT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
-
- StateMap map = state1.map();
- for (auto group : map.group()) {
- for (auto value : group.value()) {
- EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
- group.group_id());
- }
- }
-
- /*
- bucket #1 bucket #2
- | 1 2 3 4 5 6 7 8 9 10 (minutes)
- |------------------------|------------------------|--
- 1 1 1 1 1 2 1 1 2 (AppCrashEvents)
- ---------------------------------------------------SCREEN_OFF events
- | | (ScreenOffEvent = 1)
- | | (ScreenDozeEvent = 3)
- ---------------------------------------------------SCREEN_ON events
- | | (ScreenOnEvent = 2)
- | (ScreenOnSuspendEvent = 6)
- ---------------------------------------------------PROCESS STATE events
- 1 2 (TopEvent = 1002)
- 1 (ForegroundServiceEvent = 1003)
- 2 (ImportantBackgroundEvent = 1006)
- 1 1 1 (ImportantForegroundEvent = 1005)
-
- Based on the diagram above, Screen State / Process State pairs for each
- AppCrashEvent are:
- - StateTracker::kStateUnknown / important foreground
- - off / important foreground
- - off / Top
- - on / important foreground
- - off / important foreground
- - off / top
-
- - off / important foreground
- - off / foreground service
- - on / important background
-
- */
- // Initialize log events - first bucket.
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 5 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:15
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 30 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 90 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 160 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:50
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 210 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55
-
- // Initialize log events - second bucket.
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 380 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 390 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 420 * NS_PER_SEC, 2 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:10
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 440 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 7:30
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 450 * NS_PER_SEC, 1 /*uid*/)); // 7:40
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 520 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 8:50
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10
- events.push_back(
- CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40
-
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(6, countMetrics.data_size());
-
- // For each CountMetricData, check StateValue info is correct and buckets
- // have correct counts.
- auto data = countMetrics.data(0);
- ASSERT_EQ(2, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1, data.slice_by_state(0).value());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
- EXPECT_TRUE(data.slice_by_state(1).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-
- data = countMetrics.data(1);
- ASSERT_EQ(2, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
- EXPECT_TRUE(data.slice_by_state(1).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-
- data = countMetrics.data(2);
- ASSERT_EQ(2, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
- EXPECT_TRUE(data.slice_by_state(1).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-
- data = countMetrics.data(3);
- ASSERT_EQ(2, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
- EXPECT_TRUE(data.slice_by_state(1).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(2, data.bucket_info(0).count());
-
- data = countMetrics.data(4);
- ASSERT_EQ(2, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
- EXPECT_TRUE(data.slice_by_state(1).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
-
- data = countMetrics.data(5);
- ASSERT_EQ(2, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
- EXPECT_TRUE(data.slice_by_state(1).has_value());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(2, data.bucket_info(0).count());
- EXPECT_EQ(1, data.bucket_info(1).count());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
deleted file mode 100644
index 4efb038e538d..000000000000
--- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ /dev/null
@@ -1,1473 +0,0 @@
-// 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 <vector>
-
-#include "src/StatsLogProcessor.h"
-#include "src/state/StateTracker.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-TEST(DurationMetricE2eTest, TestOneBucket) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = screenOnMatcher;
- *config.add_atom_matcher() = screenOffMatcher;
-
- auto durationPredicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = durationPredicate;
-
- int64_t metricId = 123456;
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(metricId);
- durationMetric->set_what(durationPredicate.id());
- durationMetric->set_bucket(FIVE_MINUTES);
- durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- const int64_t baseTimeNs = 0; // 0:00
- const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
- const int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
-
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
-
- std::unique_ptr<LogEvent> event;
-
- // Screen is off at start of bucket.
- event = CreateScreenStateChangedEvent(configAddedTimeNs,
- android::view::DISPLAY_STATE_OFF); // 0:01
- processor->OnLogEvent(event.get());
-
- // Turn screen on.
- const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
- event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(event.get());
-
- // Turn off screen 30 seconds after turning on.
- const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
- event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(event.get());
-
- event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42
- processor->OnLogEvent(event.get());
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
- ADB_DUMP, FAST, &buffer); // 5:01
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
-
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(1, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestTwoBuckets) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = screenOnMatcher;
- *config.add_atom_matcher() = screenOffMatcher;
-
- auto durationPredicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = durationPredicate;
-
- int64_t metricId = 123456;
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(metricId);
- durationMetric->set_what(durationPredicate.id());
- durationMetric->set_bucket(FIVE_MINUTES);
- durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- const int64_t baseTimeNs = 0; // 0:00
- const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
- const int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
-
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
-
- std::unique_ptr<LogEvent> event;
-
- // Screen is off at start of bucket.
- event = CreateScreenStateChangedEvent(configAddedTimeNs,
- android::view::DISPLAY_STATE_OFF); // 0:01
- processor->OnLogEvent(event.get());
-
- // Turn screen on.
- const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
- event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(event.get());
-
- // Turn off screen 30 seconds after turning on.
- const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
- event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(event.get());
-
- event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42
- processor->OnLogEvent(event.get());
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false,
- true, ADB_DUMP, FAST, &buffer); // 10:01
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
-
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(1, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- ASSERT_EQ(1, data.bucket_info_size());
-
- auto bucketInfo = data.bucket_info(0);
- EXPECT_EQ(0, bucketInfo.bucket_num());
- EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
- EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestWithActivation) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
- auto crashMatcher = CreateProcessCrashAtomMatcher();
- *config.add_atom_matcher() = screenOnMatcher;
- *config.add_atom_matcher() = screenOffMatcher;
- *config.add_atom_matcher() = crashMatcher;
-
- auto durationPredicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = durationPredicate;
-
- int64_t metricId = 123456;
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(metricId);
- durationMetric->set_what(durationPredicate.id());
- durationMetric->set_bucket(FIVE_MINUTES);
- durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- auto metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(metricId);
- auto event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(crashMatcher.id());
- event_activation1->set_ttl_seconds(30); // 30 secs.
-
- const int64_t bucketStartTimeNs = 10000000000;
- const int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- vector<int64_t> activeConfigsBroadcast;
-
- int broadcastCount = 0;
- StatsLogProcessor processor(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
- [](const ConfigKey& key) { return true; },
- [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
- broadcastCount++;
- EXPECT_EQ(broadcastUid, uid);
- activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
- activeConfigs.end());
- return true;
- });
-
- processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); // 0:00
-
- ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
-
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- ASSERT_EQ(eventActivationMap.size(), 1u);
- EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- std::unique_ptr<LogEvent> event;
-
- // Turn screen off.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * NS_PER_SEC,
- android::view::DISPLAY_STATE_OFF); // 0:02
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC);
-
- // Turn screen on.
- const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05
- event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), durationStartNs);
-
- // Activate metric.
- const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10
- const int64_t activationEndNs =
- activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40
- event = CreateAppCrashEvent(activationStartNs, 111);
- processor.OnLogEvent(event.get(), activationStartNs);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 1);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- // Expire activation.
- const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC;
- event = CreateScreenBrightnessChangedEvent(expirationNs, 64); // 0:47
- processor.OnLogEvent(event.get(), expirationNs);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 2);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- ASSERT_EQ(eventActivationMap.size(), 1u);
- EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- // Turn off screen 10 seconds after activation expiration.
- const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50
- event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
- processor.OnLogEvent(event.get(), durationEndNs);
-
- // Turn screen on.
- const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55
- event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), duration2StartNs);
-
- // Turn off screen.
- const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05
- event = CreateScreenStateChangedEvent(duration2EndNs, android::view::DISPLAY_STATE_OFF);
- processor.OnLogEvent(event.get(), duration2EndNs);
-
- // Activate metric.
- const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10
- const int64_t activation2EndNs =
- activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40
- event = CreateAppCrashEvent(activation2StartNs, 211);
- processor.OnLogEvent(event.get(), activation2StartNs);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
- ADB_DUMP, FAST, &buffer); // 5:01
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
-
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(1, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- ASSERT_EQ(1, data.bucket_info_size());
-
- auto bucketInfo = data.bucket_info(0);
- EXPECT_EQ(0, bucketInfo.bucket_num());
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestWithCondition) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
- *config.add_predicate() = isInBackgroundPredicate;
-
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("WakelockDuration"));
- durationMetric->set_what(holdingWakelockPredicate.id());
- durationMetric->set_condition(isInBackgroundPredicate.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- durationMetric->set_bucket(FIVE_MINUTES);
-
- ConfigKey cfgKey;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_TRUE(eventActivationMap.empty());
-
- int appUid = 123;
- vector<int> attributionUids1 = {appUid};
- vector<string> attributionTags1 = {"App1"};
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
- attributionTags1,
- "wl1"); // 0:10
- processor->OnLogEvent(event.get());
-
- event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22
- processor->OnLogEvent(event.get());
-
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
- appUid); // 3:15
- processor->OnLogEvent(event.get());
-
- event = CreateReleaseWakelockEvent(bucketStartTimeNs + 4 * 60 * NS_PER_SEC, attributionUids1,
- attributionTags1,
- "wl1"); // 4:00
- processor->OnLogEvent(event.get());
-
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(1, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
-
- // Validate bucket info.
- ASSERT_EQ(1, data.bucket_info_size());
-
- auto bucketInfo = data.bucket_info(0);
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- // The predicate is dimensioning by first attribution node by uid.
- FieldMatcher dimensions = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED,
- {Position::FIRST});
- *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
- *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
- *config.add_predicate() = isInBackgroundPredicate;
-
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("WakelockDuration"));
- durationMetric->set_what(holdingWakelockPredicate.id());
- durationMetric->set_condition(isInBackgroundPredicate.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- // The metric is dimensioning by first attribution node and only by uid.
- *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- durationMetric->set_bucket(FIVE_MINUTES);
-
- // Links between wakelock state atom and condition of app is in background.
- auto links = durationMetric->add_links();
- links->set_condition(isInBackgroundPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
-
- ConfigKey cfgKey;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_TRUE(eventActivationMap.empty());
-
- int appUid = 123;
- std::vector<int> attributionUids1 = {appUid};
- std::vector<string> attributionTags1 = {"App1"};
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
- attributionTags1, "wl1"); // 0:10
- processor->OnLogEvent(event.get());
-
- event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22
- processor->OnLogEvent(event.get());
-
- event = CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1,
- attributionTags1, "wl1"); // 1:00
- processor->OnLogEvent(event.get());
-
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
- appUid); // 3:15
- processor->OnLogEvent(event.get());
-
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(1, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- // Validate dimension value.
- ValidateAttributionUidDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, appUid);
- // Validate bucket info.
- ASSERT_EQ(1, data.bucket_info_size());
-
- auto bucketInfo = data.bucket_info(0);
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
- *config.add_atom_matcher() = screenOnMatcher;
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- // The predicate is dimensioning by first attribution node by uid.
- FieldMatcher dimensions = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED,
- {Position::FIRST});
- *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
- *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
- *config.add_predicate() = isInBackgroundPredicate;
-
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("WakelockDuration"));
- durationMetric->set_what(holdingWakelockPredicate.id());
- durationMetric->set_condition(isInBackgroundPredicate.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- // The metric is dimensioning by first attribution node and only by uid.
- *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- durationMetric->set_bucket(FIVE_MINUTES);
-
- // Links between wakelock state atom and condition of app is in background.
- auto links = durationMetric->add_links();
- links->set_condition(isInBackgroundPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
-
- auto metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(durationMetric->id());
- auto event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(screenOnMatcher.id());
- event_activation1->set_ttl_seconds(60 * 2); // 2 minutes.
-
- ConfigKey cfgKey;
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- ASSERT_EQ(eventActivationMap.size(), 1u);
- EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- int appUid = 123;
- std::vector<int> attributionUids1 = {appUid};
- std::vector<string> attributionTags1 = {"App1"};
-
- auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
- attributionTags1, "wl1"); // 0:10
- processor->OnLogEvent(event.get());
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22
- processor->OnLogEvent(event.get());
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30
- event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(event.get());
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
- EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- const int64_t durationEndNs =
- durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00
- event = CreateAppCrashEvent(durationEndNs, 333);
- processor->OnLogEvent(event.get());
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
- EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
- appUid); // 3:15
- processor->OnLogEvent(event.get());
-
- event = CreateReleaseWakelockEvent(bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC,
- attributionUids1, attributionTags1, "wl1"); // 4:17
- processor->OnLogEvent(event.get());
-
- event = CreateMoveToBackgroundEvent(bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC,
- appUid); // 4:20
- processor->OnLogEvent(event.get());
-
- event = CreateAcquireWakelockEvent(bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC,
- attributionUids1, attributionTags1, "wl1"); // 4:25
- processor->OnLogEvent(event.get());
-
- const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30
- event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(event.get());
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs);
- EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
-
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(1, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- // Validate dimension value.
- ValidateAttributionUidDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, appUid);
- // Validate bucket info.
- ASSERT_EQ(2, data.bucket_info_size());
-
- auto bucketInfo = data.bucket_info(0);
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
-
- bucketInfo = data.bucket_info(1);
- EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestWithSlicedState) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
- *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
-
- auto batterySaverModePredicate = CreateBatterySaverModePredicate();
- *config.add_predicate() = batterySaverModePredicate;
-
- auto screenState = CreateScreenState();
- *config.add_state() = screenState;
-
- // Create duration metric that slices by screen state.
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreen"));
- durationMetric->set_what(batterySaverModePredicate.id());
- durationMetric->add_slice_by_state(screenState.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- durationMetric->set_bucket(FIVE_MINUTES);
-
- // Initialize StatsLogProcessor.
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- uint64_t bucketStartTimeNs = 10000000000; // 0:10
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- EXPECT_TRUE(metricsManager->isActive());
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- EXPECT_TRUE(metricProducer->mIsActive);
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- /*
- bucket #1 bucket #2
- | 1 2 3 4 5 6 7 8 9 10 (minutes)
- |-----------------------------|-----------------------------|--
- ON OFF ON (BatterySaverMode)
- | | | (ScreenIsOnEvent)
- | | (ScreenIsOffEvent)
- | (ScreenDozeEvent)
- */
- // Initialize log events.
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 10 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 50 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:00
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 80 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 120 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 250 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
-
- // Bucket boundary.
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 310 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:20
-
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 360 * NS_PER_SEC,
- true /* include current partial bucket */, true, ADB_DUMP, FAST,
- &buffer); // 6:10
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(3, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(1);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(2);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
- *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
- *config.add_atom_matcher() = CreateBatteryStateNoneMatcher();
- *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
-
- auto batterySaverModePredicate = CreateBatterySaverModePredicate();
- *config.add_predicate() = batterySaverModePredicate;
-
- auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate();
- *config.add_predicate() = deviceUnpluggedPredicate;
-
- auto screenState = CreateScreenState();
- *config.add_state() = screenState;
-
- // Create duration metric that has a condition and slices by screen state.
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("DurationBatterySaverModeOnBatterySliceScreen"));
- durationMetric->set_what(batterySaverModePredicate.id());
- durationMetric->set_condition(deviceUnpluggedPredicate.id());
- durationMetric->add_slice_by_state(screenState.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- durationMetric->set_bucket(FIVE_MINUTES);
-
- // Initialize StatsLogProcessor.
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- uint64_t bucketStartTimeNs = 10000000000; // 0:10
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- EXPECT_TRUE(metricsManager->isActive());
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- EXPECT_TRUE(metricProducer->mIsActive);
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- /*
- bucket #1 bucket #2
- | 1 2 3 4 5 6 7 8 (minutes)
- |---------------------------------------|------------------
- ON OFF ON (BatterySaverMode)
- T F T (DeviceUnpluggedPredicate)
- | | | (ScreenIsOnEvent)
- | | | (ScreenIsOffEvent)
- | (ScreenDozeEvent)
- */
- // Initialize log events.
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 20 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:30
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 80 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:30
- events.push_back(
- CreateBatteryStateChangedEvent(bucketStartTimeNs + 110 * NS_PER_SEC,
- BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 2:00
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 145 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:35
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 170 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:00
- events.push_back(
- CreateBatteryStateChangedEvent(bucketStartTimeNs + 180 * NS_PER_SEC,
- BatteryPluggedStateEnum::BATTERY_PLUGGED_USB)); // 3:10
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 200 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:30
- events.push_back(
- CreateBatteryStateChangedEvent(bucketStartTimeNs + 230 * NS_PER_SEC,
- BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 4:00
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 260 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 4:30
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
-
- // Bucket boundary.
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 320 * NS_PER_SEC)); // 5:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 380 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 6:30
-
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 410 * NS_PER_SEC,
- true /* include current partial bucket */, true, ADB_DUMP, FAST,
- &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(3, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(1);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(60 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(2);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestWithSlicedStateMapped) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
- *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
-
- auto batterySaverModePredicate = CreateBatterySaverModePredicate();
- *config.add_predicate() = batterySaverModePredicate;
-
- int64_t screenOnId = 4444;
- int64_t screenOffId = 9876;
- auto screenStateWithMap = CreateScreenStateWithOnOffMap(screenOnId, screenOffId);
- *config.add_state() = screenStateWithMap;
-
- // Create duration metric that slices by mapped screen state.
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreenMapped"));
- durationMetric->set_what(batterySaverModePredicate.id());
- durationMetric->add_slice_by_state(screenStateWithMap.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- durationMetric->set_bucket(FIVE_MINUTES);
-
- // Initialize StatsLogProcessor.
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- uint64_t bucketStartTimeNs = 10000000000; // 0:10
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- EXPECT_TRUE(metricsManager->isActive());
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- EXPECT_TRUE(metricProducer->mIsActive);
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- /*
- bucket #1 bucket #2
- | 1 2 3 4 5 6 7 8 9 10 (minutes)
- |-----------------------------|-----------------------------|--
- ON OFF ON (BatterySaverMode)
- ---------------------------------------------------------SCREEN_OFF events
- | | (ScreenStateOffEvent = 1)
- | (ScreenStateDozeEvent = 3)
- | (ScreenStateDozeSuspendEvent = 4)
- ---------------------------------------------------------SCREEN_ON events
- | | | (ScreenStateOnEvent = 2)
- | (ScreenStateVrEvent = 5)
- | (ScreenStateOnSuspendEvent = 6)
- */
- // Initialize log events.
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 10 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 70 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:20
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 100 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:50
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 120 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 170 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:00
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 250 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
-
- // Bucket boundary 5:10.
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 320 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:30
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 390 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40
- events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 430 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 490 * NS_PER_SEC,
- true /* include current partial bucket */, true, ADB_DUMP, FAST,
- &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(2, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(130 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(1);
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(80 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-}
-
-TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto uidProcessState = CreateUidProcessState();
- *config.add_state() = uidProcessState;
-
- // Create duration metric that slices by uid process state.
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("DurationHoldingWakelockSliceUidProcessState"));
- durationMetric->set_what(holdingWakelockPredicate.id());
- durationMetric->add_slice_by_state(uidProcessState.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- durationMetric->set_bucket(FIVE_MINUTES);
-
- // The state has only one primary field (uid).
- auto stateLink = durationMetric->add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
-
- // Initialize StatsLogProcessor.
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- uint64_t bucketStartTimeNs = 10000000000; // 0:10
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // This config is rejected because the dimension in what fields are not a superset of the sliced
- // state primary fields.
- ASSERT_EQ(processor->mMetricsManagers.size(), 0);
-}
-
-TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset) {
- // Initialize config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto uidProcessState = CreateUidProcessState();
- *config.add_state() = uidProcessState;
-
- // Create duration metric that slices by uid process state.
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("DurationPartialWakelockPerTagUidSliceProcessState"));
- durationMetric->set_what(holdingWakelockPredicate.id());
- durationMetric->add_slice_by_state(uidProcessState.id());
- durationMetric->set_aggregation_type(DurationMetric::SUM);
- durationMetric->set_bucket(FIVE_MINUTES);
-
- // The metric is dimensioning by first uid of attribution node and tag.
- *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */});
- // The state has only one primary field (uid).
- auto stateLink = durationMetric->add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
-
- // Initialize StatsLogProcessor.
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- uint64_t bucketStartTimeNs = 10000000000; // 0:10
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- EXPECT_TRUE(metricsManager->isActive());
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- EXPECT_TRUE(metricProducer->mIsActive);
- ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
- EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID);
- ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-
- // Initialize log events.
- int appUid1 = 1001;
- int appUid2 = 1002;
- std::vector<int> attributionUids1 = {appUid1};
- std::vector<string> attributionTags1 = {"App1"};
-
- std::vector<int> attributionUids2 = {appUid2};
- std::vector<string> attributionTags2 = {"App2"};
-
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 10 * NS_PER_SEC, appUid1,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:20
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 20 * NS_PER_SEC,
- attributionUids1, attributionTags1,
- "wakelock1")); // 0:30
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * NS_PER_SEC,
- attributionUids1, attributionTags1,
- "wakelock2")); // 0:35
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 30 * NS_PER_SEC,
- attributionUids2, attributionTags2,
- "wakelock1")); // 0:40
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC,
- attributionUids2, attributionTags2,
- "wakelock2")); // 0:45
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 50 * NS_PER_SEC, appUid2,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:00
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 60 * NS_PER_SEC, appUid1,
- android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:10
- events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 100 * NS_PER_SEC,
- attributionUids2, attributionTags2,
- "wakelock1")); // 1:50
- events.push_back(CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 120 * NS_PER_SEC, appUid2,
- android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 2:10
- events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 200 * NS_PER_SEC,
- attributionUids1, attributionTags1,
- "wakelock2")); // 3:30
-
- // Send log events to StatsLogProcessor.
- for (auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- // Check dump report.
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 320 * NS_PER_SEC,
- true /* include current partial bucket */, true, ADB_DUMP, FAST,
- &buffer);
- ASSERT_GT(buffer.size(), 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
- StatsLogReport::DurationMetricDataWrapper durationMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
- &durationMetrics);
- ASSERT_EQ(9, durationMetrics.data_size());
-
- DurationMetricData data = durationMetrics.data(0);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
- "wakelock1");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(1);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
- "wakelock1");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(240 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(2);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
- "wakelock2");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(3);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
- "wakelock2");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(140 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(4);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
- "wakelock1");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /* StateTracker:: kStateUnknown */, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(5);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
- "wakelock1");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(6);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
- "wakelock2");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /* StateTracker:: kStateUnknown */, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(15 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(7);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
- "wakelock2");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
- data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(180 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
-
- data = durationMetrics.data(8);
- ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
- "wakelock2");
- ASSERT_EQ(1, data.slice_by_state_size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
deleted file mode 100644
index 1be261297e0a..000000000000
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ /dev/null
@@ -1,624 +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.
-
-#include <android/binder_interface_utils.h>
-#include <gtest/gtest.h>
-
-#include <vector>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-using ::ndk::SharedRefBase;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-const int64_t metricId = 123456;
-const int32_t ATOM_TAG = util::SUBSYSTEM_SLEEP_STATE;
-
-StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type,
- bool useCondition = true) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
- auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG);
- *config.add_atom_matcher() = atomMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- *config.add_predicate() = screenIsOffPredicate;
-
- auto gaugeMetric = config.add_gauge_metric();
- gaugeMetric->set_id(metricId);
- gaugeMetric->set_what(atomMatcher.id());
- if (useCondition) {
- gaugeMetric->set_condition(screenIsOffPredicate.id());
- }
- gaugeMetric->set_sampling_type(sampling_type);
- gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
- *gaugeMetric->mutable_dimensions_in_what() =
- CreateDimensions(ATOM_TAG, {1 /* subsystem name */});
- gaugeMetric->set_bucket(FIVE_MINUTES);
- gaugeMetric->set_max_pull_delay_sec(INT_MAX);
- config.set_hash_strings_in_metric_report(false);
-
- return config;
-}
-
-} // namespaces
-
-TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
- auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor =
- CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
-
- // When creating the config, the gauge metric producer should register the alarm at the
- // end of the current bucket.
- ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
- EXPECT_EQ(bucketSizeNs,
- processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
- int64_t& nextPullTimeNs =
- processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
-
- auto screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- // Pulling alarm arrives on time and reset the sequential pulling alarm.
- processor->informPullAlarmFired(nextPullTimeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
-
- auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- processor->informPullAlarmFired(nextPullTimeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
-
- processor->informPullAlarmFired(nextPullTimeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
-
- screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- processor->informPullAlarmFired(nextPullTimeNs + 3);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
-
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- processor->informPullAlarmFired(nextPullTimeNs + 2);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs);
-
- processor->informPullAlarmFired(nextPullTimeNs + 2);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- ASSERT_GT((int)gaugeMetrics.data_size(), 1);
-
- auto data = gaugeMetrics.data(0);
- EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- ASSERT_EQ(6, data.bucket_info_size());
-
- ASSERT_EQ(1, data.bucket_info(0).atom_size());
- ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(2).atom_size());
- ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(3).atom_size());
- ASSERT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, data.bucket_info(3).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(4).atom_size());
- ASSERT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(4).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(5).atom_size());
- ASSERT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, data.bucket_info(5).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(5).atom(0).subsystem_sleep_state().time_millis(), 0);
-}
-
-TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
- auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor =
- CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
-
- auto screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
- screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 3,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 10,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- ASSERT_GT((int)gaugeMetrics.data_size(), 1);
-
- auto data = gaugeMetrics.data(0);
- EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- ASSERT_EQ(3, data.bucket_info_size());
-
- ASSERT_EQ(1, data.bucket_info(0).atom_size());
- ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, data.bucket_info(1).elapsed_timestamp_nanos(0));
- EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(2, data.bucket_info(2).atom_size());
- ASSERT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, data.bucket_info(2).elapsed_timestamp_nanos(1));
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_TRUE(data.bucket_info(2).atom(1).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0);
-}
-
-TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
- auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor =
- CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
-
- // When creating the config, the gauge metric producer should register the alarm at the
- // end of the current bucket.
- ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
- EXPECT_EQ(bucketSizeNs,
- processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
- int64_t& nextPullTimeNs =
- processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
-
- auto screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- // Pulling alarm arrives one bucket size late.
- processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
-
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 11,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- // Pulling alarm arrives more than one bucket size late.
- processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- ASSERT_GT((int)gaugeMetrics.data_size(), 1);
-
- auto data = gaugeMetrics.data(0);
- EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- ASSERT_EQ(3, data.bucket_info_size());
-
- ASSERT_EQ(1, data.bucket_info(0).atom_size());
- ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
- data.bucket_info(1).elapsed_timestamp_nanos(0));
- EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(2).atom_size());
- ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, data.bucket_info(2).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
-}
-
-TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) {
- auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false);
-
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-
- auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
- *config.add_atom_matcher() = batterySaverStartMatcher;
- const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
- auto metric_activation = config.add_metric_activation();
- metric_activation->set_metric_id(metricId);
- metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
- auto event_activation = metric_activation->add_event_activation();
- event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
- event_activation->set_ttl_seconds(ttlNs / 1000000000);
-
- ConfigKey cfgKey;
- auto processor =
- CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
- EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- // When creating the config, the gauge metric producer should register the alarm at the
- // end of the current bucket.
- ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
- EXPECT_EQ(bucketSizeNs,
- processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
- int64_t& nextPullTimeNs =
- processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
-
- // Pulling alarm arrives on time and reset the sequential pulling alarm.
- // Event should not be kept.
- processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns.
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
- EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- // Activate the metric. A pull occurs upon activation.
- const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
- auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
- processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- // This event should be kept. 2 total.
- processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns.
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
-
- // This event should be kept. 3 total.
- processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns.
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
-
- // Create random event to deactivate metric.
- auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
- processor->OnLogEvent(deactivationEvent.get());
- EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- // Event should not be kept. 3 total.
- processor->informPullAlarmFired(nextPullTimeNs + 3);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
-
- processor->informPullAlarmFired(nextPullTimeNs + 2);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- ASSERT_GT((int)gaugeMetrics.data_size(), 0);
-
- auto data = gaugeMetrics.data(0);
- EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- ASSERT_EQ(3, data.bucket_info_size());
-
- auto bucketInfo = data.bucket_info(0);
- ASSERT_EQ(1, bucketInfo.atom_size());
- ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
- EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
-
- bucketInfo = data.bucket_info(1);
- ASSERT_EQ(1, bucketInfo.atom_size());
- ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
-
- bucketInfo = data.bucket_info(2);
- ASSERT_EQ(1, bucketInfo.atom_size());
- ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
- EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)),
- bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)),
- bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
-}
-
-TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) {
- auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE, /*useCondition=*/false);
-
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(),
- ATOM_TAG);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
-
- // When creating the config, the gauge metric producer should register the alarm at the
- // end of the current bucket.
- ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
- EXPECT_EQ(bucketSizeNs,
- processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
- int64_t& nextPullTimeNs =
- processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
-
- // Pulling alarm arrives on time and reset the sequential pulling alarm.
- processor->informPullAlarmFired(nextPullTimeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
-
- processor->informPullAlarmFired(nextPullTimeNs + 4);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
- nextPullTimeNs);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- ASSERT_GT((int)gaugeMetrics.data_size(), 0);
-
- auto data = gaugeMetrics.data(0);
- EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- ASSERT_EQ(3, data.bucket_info_size());
-
- ASSERT_EQ(1, data.bucket_info(0).atom_size());
- ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(1).atom_size());
- ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, data.bucket_info(1).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
-
- ASSERT_EQ(1, data.bucket_info(2).atom_size());
- ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 4, data.bucket_info(2).elapsed_timestamp_nanos(0));
- ASSERT_EQ(0, data.bucket_info(2).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
deleted file mode 100644
index a40a9484a841..000000000000
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ /dev/null
@@ -1,295 +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.
-
-#include <gtest/gtest.h>
-
-#include <vector>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateStatsdConfigForPushedEvent(const GaugeMetric::SamplingType sampling_type) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-
- auto atomMatcher = CreateSimpleAtomMatcher("", util::APP_START_OCCURRED);
- *config.add_atom_matcher() = atomMatcher;
-
- auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
- *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
- *config.add_predicate() = isInBackgroundPredicate;
-
- auto gaugeMetric = config.add_gauge_metric();
- gaugeMetric->set_id(123456);
- gaugeMetric->set_what(atomMatcher.id());
- gaugeMetric->set_condition(isInBackgroundPredicate.id());
- gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false);
- gaugeMetric->set_sampling_type(sampling_type);
- auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
- fieldMatcher->set_field(util::APP_START_OCCURRED);
- fieldMatcher->add_child()->set_field(3); // type (enum)
- fieldMatcher->add_child()->set_field(4); // activity_name(str)
- fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64)
- *gaugeMetric->mutable_dimensions_in_what() =
- CreateDimensions(util::APP_START_OCCURRED, {1 /* uid field */ });
- gaugeMetric->set_bucket(FIVE_MINUTES);
-
- auto links = gaugeMetric->add_links();
- links->set_condition(isInBackgroundPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(util::APP_START_OCCURRED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- auto dimensionCondition = links->mutable_fields_in_condition();
- dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- dimensionCondition->add_child()->set_field(1); // uid field.
- return config;
-}
-
-std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
- uint64_t timestampNs, const int uid, const string& pkg_name,
- AppStartOccurred::TransitionType type, const string& activity_name,
- const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::APP_START_OCCURRED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeString(statsEvent, pkg_name.c_str());
- AStatsEvent_writeInt32(statsEvent, type);
- AStatsEvent_writeString(statsEvent, activity_name.c_str());
- AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str());
- AStatsEvent_writeInt32(statsEvent, is_instant_app);
- AStatsEvent_writeInt32(statsEvent, activity_start_msec);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-} // namespace
-
-TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) {
- for (const auto& sampling_type :
- {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) {
- auto config = CreateStatsdConfigForPushedEvent(sampling_type);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor =
- CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- int appUid1 = 123;
- int appUid2 = 456;
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid1));
- events.push_back(
- CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid1));
- events.push_back(
- CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid1));
- events.push_back(
- CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, appUid1));
-
- events.push_back(CreateAppStartOccurredEvent(
- bucketStartTimeNs + 10, appUid1, "app1", AppStartOccurred::WARM, "activity_name1",
- "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/));
- events.push_back(CreateAppStartOccurredEvent(
- bucketStartTimeNs + 20, appUid1, "app1", AppStartOccurred::HOT, "activity_name2",
- "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/));
- events.push_back(CreateAppStartOccurredEvent(
- bucketStartTimeNs + 30, appUid1, "app1", AppStartOccurred::COLD, "activity_name3",
- "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/));
- events.push_back(CreateAppStartOccurredEvent(
- bucketStartTimeNs + bucketSizeNs + 30, appUid1, "app1", AppStartOccurred::WARM,
- "activity_name4", "calling_pkg_name4", true /*is_instant_app*/,
- 104 /*activity_start_msec*/));
- events.push_back(CreateAppStartOccurredEvent(
- bucketStartTimeNs + 2 * bucketSizeNs, appUid1, "app1", AppStartOccurred::COLD,
- "activity_name5", "calling_pkg_name5", true /*is_instant_app*/,
- 105 /*activity_start_msec*/));
- events.push_back(CreateAppStartOccurredEvent(
- bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid1, "app1", AppStartOccurred::HOT,
- "activity_name6", "calling_pkg_name6", false /*is_instant_app*/,
- 106 /*activity_start_msec*/));
-
- events.push_back(
- CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 10, appUid2));
- events.push_back(CreateAppStartOccurredEvent(
- bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid2, "app2", AppStartOccurred::COLD,
- "activity_name7", "calling_pkg_name7", true /*is_instant_app*/,
- 201 /*activity_start_msec*/));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(),
- &gaugeMetrics);
- ASSERT_EQ(2, gaugeMetrics.data_size());
-
- auto data = gaugeMetrics.data(0);
- EXPECT_EQ(util::APP_START_OCCURRED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(3, data.bucket_info_size());
- if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) {
- ASSERT_EQ(2, data.bucket_info(0).atom_size());
- ASSERT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
- ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(AppStartOccurred::HOT,
- data.bucket_info(0).atom(0).app_start_occurred().type());
- EXPECT_EQ("activity_name2",
- data.bucket_info(0).atom(0).app_start_occurred().activity_name());
- EXPECT_EQ(102L,
- data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
- EXPECT_EQ(AppStartOccurred::COLD,
- data.bucket_info(0).atom(1).app_start_occurred().type());
- EXPECT_EQ("activity_name3",
- data.bucket_info(0).atom(1).app_start_occurred().activity_name());
- EXPECT_EQ(103L,
- data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis());
-
- ASSERT_EQ(1, data.bucket_info(1).atom_size());
- ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_EQ(AppStartOccurred::WARM,
- data.bucket_info(1).atom(0).app_start_occurred().type());
- EXPECT_EQ("activity_name4",
- data.bucket_info(1).atom(0).app_start_occurred().activity_name());
- EXPECT_EQ(104L,
- data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
-
- ASSERT_EQ(2, data.bucket_info(2).atom_size());
- ASSERT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_EQ(AppStartOccurred::COLD,
- data.bucket_info(2).atom(0).app_start_occurred().type());
- EXPECT_EQ("activity_name5",
- data.bucket_info(2).atom(0).app_start_occurred().activity_name());
- EXPECT_EQ(105L,
- data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
- EXPECT_EQ(AppStartOccurred::HOT,
- data.bucket_info(2).atom(1).app_start_occurred().type());
- EXPECT_EQ("activity_name6",
- data.bucket_info(2).atom(1).app_start_occurred().activity_name());
- EXPECT_EQ(106L,
- data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis());
- } else {
- ASSERT_EQ(1, data.bucket_info(0).atom_size());
- ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(AppStartOccurred::HOT,
- data.bucket_info(0).atom(0).app_start_occurred().type());
- EXPECT_EQ("activity_name2",
- data.bucket_info(0).atom(0).app_start_occurred().activity_name());
- EXPECT_EQ(102L,
- data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
-
- ASSERT_EQ(1, data.bucket_info(1).atom_size());
- ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_EQ(AppStartOccurred::WARM,
- data.bucket_info(1).atom(0).app_start_occurred().type());
- EXPECT_EQ("activity_name4",
- data.bucket_info(1).atom(0).app_start_occurred().activity_name());
- EXPECT_EQ(104L,
- data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
-
- ASSERT_EQ(1, data.bucket_info(2).atom_size());
- ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_EQ(AppStartOccurred::COLD,
- data.bucket_info(2).atom(0).app_start_occurred().type());
- EXPECT_EQ("activity_name5",
- data.bucket_info(2).atom(0).app_start_occurred().activity_name());
- EXPECT_EQ(105L,
- data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
- }
-
- data = gaugeMetrics.data(1);
-
- EXPECT_EQ(data.dimensions_in_what().field(), util::APP_START_OCCURRED);
- ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- ASSERT_EQ(1, data.bucket_info(0).atom_size());
- ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type());
- EXPECT_EQ("activity_name7",
- data.bucket_info(0).atom(0).app_start_occurred().activity_name());
- EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
- }
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
deleted file mode 100644
index e320419a9d44..000000000000
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ /dev/null
@@ -1,1833 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateStatsdConfig() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
- auto crashMatcher = CreateProcessCrashAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-
- *config.add_atom_matcher() = saverModeMatcher;
- *config.add_atom_matcher() = crashMatcher;
- *config.add_atom_matcher() = screenOnMatcher;
-
- int64_t metricId = 123456;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(metricId);
- countMetric->set_what(crashMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- countMetric->mutable_dimensions_in_what()->set_field(
- util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
-
- auto metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(metricId);
- auto event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(saverModeMatcher.id());
- event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
- auto event_activation2 = metric_activation1->add_event_activation();
- event_activation2->set_atom_matcher_id(screenOnMatcher.id());
- event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
-
- return config;
-}
-
-StatsdConfig CreateStatsdConfigWithOneDeactivation() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
- auto crashMatcher = CreateProcessCrashAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
-
- *config.add_atom_matcher() = saverModeMatcher;
- *config.add_atom_matcher() = crashMatcher;
- *config.add_atom_matcher() = screenOnMatcher;
- *config.add_atom_matcher() = brightnessChangedMatcher;
-
- int64_t metricId = 123456;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(metricId);
- countMetric->set_what(crashMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- countMetric->mutable_dimensions_in_what()->set_field(
- util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
-
- auto metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(metricId);
- auto event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(saverModeMatcher.id());
- event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
- event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
- auto event_activation2 = metric_activation1->add_event_activation();
- event_activation2->set_atom_matcher_id(screenOnMatcher.id());
- event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
-
- return config;
-}
-
-StatsdConfig CreateStatsdConfigWithTwoDeactivations() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
- auto crashMatcher = CreateProcessCrashAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
- auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher();
- brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2"));
-
- *config.add_atom_matcher() = saverModeMatcher;
- *config.add_atom_matcher() = crashMatcher;
- *config.add_atom_matcher() = screenOnMatcher;
- *config.add_atom_matcher() = brightnessChangedMatcher;
- *config.add_atom_matcher() = brightnessChangedMatcher2;
-
- int64_t metricId = 123456;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(metricId);
- countMetric->set_what(crashMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- countMetric->mutable_dimensions_in_what()->set_field(
- util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
-
- auto metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(metricId);
- auto event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(saverModeMatcher.id());
- event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
- event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
- auto event_activation2 = metric_activation1->add_event_activation();
- event_activation2->set_atom_matcher_id(screenOnMatcher.id());
- event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
- event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id());
-
- return config;
-}
-
-StatsdConfig CreateStatsdConfigWithSameDeactivations() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
- auto crashMatcher = CreateProcessCrashAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
-
- *config.add_atom_matcher() = saverModeMatcher;
- *config.add_atom_matcher() = crashMatcher;
- *config.add_atom_matcher() = screenOnMatcher;
- *config.add_atom_matcher() = brightnessChangedMatcher;
-
- int64_t metricId = 123456;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(metricId);
- countMetric->set_what(crashMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- countMetric->mutable_dimensions_in_what()->set_field(
- util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
-
- auto metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(metricId);
- auto event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(saverModeMatcher.id());
- event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
- event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
- auto event_activation2 = metric_activation1->add_event_activation();
- event_activation2->set_atom_matcher_id(screenOnMatcher.id());
- event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
- event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
-
- return config;
-}
-
-StatsdConfig CreateStatsdConfigWithTwoMetricsTwoDeactivations() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
- auto crashMatcher = CreateProcessCrashAtomMatcher();
- auto foregroundMatcher = CreateMoveToForegroundAtomMatcher();
- auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- auto brightnessChangedMatcher = CreateScreenBrightnessChangedAtomMatcher();
- auto brightnessChangedMatcher2 = CreateScreenBrightnessChangedAtomMatcher();
- brightnessChangedMatcher2.set_id(StringToId("ScreenBrightnessChanged2"));
-
- *config.add_atom_matcher() = saverModeMatcher;
- *config.add_atom_matcher() = crashMatcher;
- *config.add_atom_matcher() = screenOnMatcher;
- *config.add_atom_matcher() = brightnessChangedMatcher;
- *config.add_atom_matcher() = brightnessChangedMatcher2;
- *config.add_atom_matcher() = foregroundMatcher;
-
- int64_t metricId = 123456;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(metricId);
- countMetric->set_what(crashMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- countMetric->mutable_dimensions_in_what()->set_field(
- util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
-
- int64_t metricId2 = 234567;
- countMetric = config.add_count_metric();
- countMetric->set_id(metricId2);
- countMetric->set_what(foregroundMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- countMetric->mutable_dimensions_in_what()->set_field(
- util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
-
- auto metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(metricId);
- auto event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(saverModeMatcher.id());
- event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
- event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
- auto event_activation2 = metric_activation1->add_event_activation();
- event_activation2->set_atom_matcher_id(screenOnMatcher.id());
- event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
- event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id());
-
- metric_activation1 = config.add_metric_activation();
- metric_activation1->set_metric_id(metricId2);
- event_activation1 = metric_activation1->add_event_activation();
- event_activation1->set_atom_matcher_id(saverModeMatcher.id());
- event_activation1->set_ttl_seconds(60 * 6); // 6 minutes
- event_activation1->set_deactivation_atom_matcher_id(brightnessChangedMatcher.id());
- event_activation2 = metric_activation1->add_event_activation();
- event_activation2->set_atom_matcher_id(screenOnMatcher.id());
- event_activation2->set_ttl_seconds(60 * 2); // 2 minutes
- event_activation2->set_deactivation_atom_matcher_id(brightnessChangedMatcher2.id());
-
- return config;
-}
-
-} // namespace
-
-TEST(MetricActivationE2eTest, TestCountMetric) {
- auto config = CreateStatsdConfig();
-
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- vector<int64_t> activeConfigsBroadcast;
-
- long timeBase1 = 1;
- int broadcastCount = 0;
- StatsLogProcessor processor(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
- [](const ConfigKey& key) { return true; },
- [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
- broadcastCount++;
- EXPECT_EQ(broadcastUid, uid);
- activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
- activeConfigs.end());
- return true;
- });
-
- processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
-
- ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
-
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
- // triggered by screen on event (tracker index 2).
- ASSERT_EQ(eventActivationMap.size(), 2u);
- EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
- EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-
- std::unique_ptr<LogEvent> event;
-
- event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 0);
-
- // Activated by battery save mode.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 1);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-
- // First processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-
- // Activated by screen on event.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-
- // 2nd processed event.
- // The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- // No new broadcast since the config should still be active.
- EXPECT_EQ(broadcastCount, 1);
-
- // 3rd processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-
- // All activations expired.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 2);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-
- // Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
- android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
-
- // 4th processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
-
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(4, countMetrics.data_size());
-
- auto data = countMetrics.data(0);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(1);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(2);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- // Partial bucket as metric is deactivated.
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(3);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
- auto config = CreateStatsdConfigWithOneDeactivation();
-
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- vector<int64_t> activeConfigsBroadcast;
-
- long timeBase1 = 1;
- int broadcastCount = 0;
- StatsLogProcessor processor(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
- [](const ConfigKey& key) { return true; },
- [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
- broadcastCount++;
- EXPECT_EQ(broadcastUid, uid);
- activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
- activeConfigs.end());
- return true;
- });
-
- processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
-
- ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
- auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
-
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
- // triggered by screen on event (tracker index 2).
- ASSERT_EQ(eventActivationMap.size(), 2u);
- EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
- EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- ASSERT_EQ(eventDeactivationMap.size(), 1u);
- EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
- ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- std::unique_ptr<LogEvent> event;
-
- event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 0);
-
- // Activated by battery save mode.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 1);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- // First processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-
- // Activated by screen on event.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- // 2nd processed event.
- // The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- // No new broadcast since the config should still be active.
- EXPECT_EQ(broadcastCount, 1);
-
- // 3rd processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-
- // All activations expired.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 2);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- // Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
- android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- // 4th processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-
- // Re-enable battery saver mode activation.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- // 5th processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-
- // Cancel battery saver mode activation.
- event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- // Screen-on activation expired.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 4);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-
- // Re-enable battery saver mode activation.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 5);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- // Cancel battery saver mode activation.
- event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 6);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
-
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(5, countMetrics.data_size());
-
- auto data = countMetrics.data(0);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(1);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(2);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- // Partial bucket as metric is deactivated.
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(3);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(4);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 13,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
- auto config = CreateStatsdConfigWithTwoDeactivations();
-
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- vector<int64_t> activeConfigsBroadcast;
-
- long timeBase1 = 1;
- int broadcastCount = 0;
- StatsLogProcessor processor(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
- [](const ConfigKey& key) { return true; },
- [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
- broadcastCount++;
- EXPECT_EQ(broadcastUid, uid);
- activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
- activeConfigs.end());
- return true;
- });
-
- processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
-
- ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
- auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
-
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
- // triggered by screen on event (tracker index 2).
- ASSERT_EQ(eventActivationMap.size(), 2u);
- EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
- EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- ASSERT_EQ(eventDeactivationMap.size(), 2u);
- EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
- EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
- ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
- ASSERT_EQ(eventDeactivationMap[4].size(), 1u);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- std::unique_ptr<LogEvent> event;
-
- event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 0);
-
- // Activated by battery save mode.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 1);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- // First processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-
- // Activated by screen on event.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- // 2nd processed event.
- // The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- // No new broadcast since the config should still be active.
- EXPECT_EQ(broadcastCount, 1);
-
- // 3rd processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-
- // All activations expired.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 2);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- // Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
- android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- // 4th processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-
- // Re-enable battery saver mode activation.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- // 5th processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-
- // Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 4);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- // Screen-on activation expired.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 4);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-
- // Re-enable battery saver mode activation.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 5);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- // Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 6);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
-
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(5, countMetrics.data_size());
-
- auto data = countMetrics.data(0);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(1);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(2);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- // Partial bucket as metric is deactivated.
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(3);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(4);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) {
- auto config = CreateStatsdConfigWithSameDeactivations();
-
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- vector<int64_t> activeConfigsBroadcast;
-
- long timeBase1 = 1;
- int broadcastCount = 0;
- StatsLogProcessor processor(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
- [](const ConfigKey& key) { return true; },
- [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
- broadcastCount++;
- EXPECT_EQ(broadcastUid, uid);
- activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
- activeConfigs.end());
- return true;
- });
-
- processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
-
- ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
- auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
-
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
- // triggered by screen on event (tracker index 2).
- ASSERT_EQ(eventActivationMap.size(), 2u);
- EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
- EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- ASSERT_EQ(eventDeactivationMap.size(), 1u);
- EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
- ASSERT_EQ(eventDeactivationMap[3].size(), 2u);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]);
- EXPECT_EQ(broadcastCount, 0);
-
- std::unique_ptr<LogEvent> event;
-
- // Event that should be ignored.
- event = CreateAppCrashEvent(bucketStartTimeNs + 1, 111);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 1);
-
- // Activate metric via screen on for 2 minutes.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 1);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
-
- // 1st processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-
- // Enable battery saver mode activation for 5 minutes.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 + 10);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
-
- // 2nd processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 40, 333);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40);
-
- // Cancel battery saver mode and screen on activation.
- int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61;
- event = CreateScreenBrightnessChangedEvent(firstDeactivation, 64);
- processor.OnLogEvent(event.get(), firstDeactivation);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 2);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-
- // Should be ignored
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 61 + 80, 444);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80);
-
- // Re-enable battery saver mode activation.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-
- // 3rd processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80, 555);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
-
- // Cancel battery saver mode activation.
- int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13;
- event = CreateScreenBrightnessChangedEvent(secondDeactivation, 140);
- processor.OnLogEvent(event.get(), secondDeactivation);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(broadcastCount, 4);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
-
- // Should be ignored.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80, 666);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
-
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(3, countMetrics.data_size());
-
- auto data = countMetrics.data(0);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(1);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(2);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- // Partial bucket as metric is deactivated.
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
- auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations();
-
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
-
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- sp<UidMap> m = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> subscriberAlarmMonitor;
- vector<int64_t> activeConfigsBroadcast;
-
- long timeBase1 = 1;
- int broadcastCount = 0;
- StatsLogProcessor processor(
- m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
- [](const ConfigKey& key) { return true; },
- [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
- broadcastCount++;
- EXPECT_EQ(broadcastUid, uid);
- activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
- activeConfigs.end());
- return true;
- });
-
- processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
-
- ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 2);
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- auto& eventActivationMap = metricProducer->mEventActivationMap;
- auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
- sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1];
- auto& eventActivationMap2 = metricProducer2->mEventActivationMap;
- auto& eventDeactivationMap2 = metricProducer2->mEventDeactivationMap;
-
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_FALSE(metricProducer2->mIsActive);
- // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
- // triggered by screen on event (tracker index 2).
- ASSERT_EQ(eventActivationMap.size(), 2u);
- EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
- EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- ASSERT_EQ(eventDeactivationMap.size(), 2u);
- EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
- EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
- ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
- ASSERT_EQ(eventDeactivationMap[4].size(), 1u);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
-
- ASSERT_EQ(eventActivationMap2.size(), 2u);
- EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end());
- EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end());
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, 0);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- ASSERT_EQ(eventDeactivationMap2.size(), 2u);
- EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end());
- EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end());
- ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
- ASSERT_EQ(eventDeactivationMap[4].size(), 1u);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- std::unique_ptr<LogEvent> event;
-
- event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + 5, 1111);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_FALSE(metricProducer2->mIsActive);
- EXPECT_EQ(broadcastCount, 0);
-
- // Activated by battery save mode.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_EQ(broadcastCount, 1);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_TRUE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- // First processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + 15, 2222);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
-
- // Activated by screen on event.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_TRUE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- // 2nd processed event.
- // The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 3333);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_TRUE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
- // No new broadcast since the config should still be active.
- EXPECT_EQ(broadcastCount, 1);
-
- // 3rd processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 4444);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
-
- // All activations expired.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 5555);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- EXPECT_FALSE(metricsManager->isActive());
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 2);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_FALSE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- // Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
- android::view::DISPLAY_STATE_ON);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_TRUE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- // 4th processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 6666);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
-
- // Re-enable battery saver mode activation.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_EQ(broadcastCount, 3);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_TRUE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- // 5th processed event.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 7777);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
-
- // Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- EXPECT_FALSE(metricsManager->isActive());
- // New broadcast since the config is no longer active.
- EXPECT_EQ(broadcastCount, 4);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_FALSE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- // Screen-on activation expired.
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 8888);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_EQ(broadcastCount, 4);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_FALSE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 9999);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
-
- // Re-enable battery saver mode activation.
- event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_TRUE(metricsManager->isActive());
- EXPECT_EQ(broadcastCount, 5);
- ASSERT_EQ(activeConfigsBroadcast.size(), 1);
- EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
- EXPECT_TRUE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_TRUE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- // Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
- processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- EXPECT_FALSE(metricsManager->isActive());
- EXPECT_EQ(broadcastCount, 6);
- ASSERT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_FALSE(metricProducer2->mIsActive);
- EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
- EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
- EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
- EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(2, reports.reports(0).metrics_size());
-
- StatsLogReport::CountMetricDataWrapper countMetrics;
-
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- ASSERT_EQ(5, countMetrics.data_size());
-
- auto data = countMetrics.data(0);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(1);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(2);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- // Partial bucket as metric is deactivated.
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(3);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(4);
- EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- countMetrics.clear_data();
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).count_metrics(), &countMetrics);
- ASSERT_EQ(5, countMetrics.data_size());
-
- data = countMetrics.data(0);
- EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(1);
- EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(2);
- EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- // Partial bucket as metric is deactivated.
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(3);
- EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-
- data = countMetrics.data(4);
- EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* uid field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
- data.bucket_info(0).end_bucket_elapsed_nanos());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
deleted file mode 100644
index 5e77ee0f0b0a..000000000000
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ /dev/null
@@ -1,348 +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.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-namespace {
-
-StatsdConfig CreateStatsdConfig() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
- *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-
- auto appCrashMatcher = CreateProcessCrashAtomMatcher();
- *config.add_atom_matcher() = appCrashMatcher;
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidDimensions(
- util::SYNC_STATE_CHANGED, {Position::FIRST});
- syncDimension->add_child()->set_field(2 /* name field*/);
-
- auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
- *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
-
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- *config.add_predicate() = isInBackgroundPredicate;
-
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(StringToId("combinationPredicate"));
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate);
-
- auto countMetric = config.add_count_metric();
- countMetric->set_id(StringToId("AppCrashes"));
- countMetric->set_what(appCrashMatcher.id());
- countMetric->set_condition(combinationPredicate->id());
- // The metric is dimensioning by uid only.
- *countMetric->mutable_dimensions_in_what() =
- CreateDimensions(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
- countMetric->set_bucket(FIVE_MINUTES);
-
- // Links between crash atom and condition of app is in syncing.
- auto links = countMetric->add_links();
- links->set_condition(isSyncingPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- util::SYNC_STATE_CHANGED, {Position::FIRST});
-
- // Links between crash atom and condition of app is in background.
- links = countMetric->add_links();
- links->set_condition(isInBackgroundPredicate.id());
- dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- auto dimensionCondition = links->mutable_fields_in_condition();
- dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- dimensionCondition->add_child()->set_field(1); // uid field.
- return config;
-}
-} // namespace
-
-// If we want to test multiple dump data, we must do it in separate tests, because in the e2e tests,
-// we should use the real API which will clear the data after dump data is called.
-TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks1) {
- auto config = CreateStatsdConfig();
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- int appUid = 123;
- auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid);
- auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid);
- auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid);
-
- auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid);
- auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid);
- auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid);
-
- auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid);
- auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid);
-
- auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid);
- auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid);
-
- auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- auto screenTurnedOnEvent2 =
- CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-
- std::vector<int> attributionUids = {appUid, appUid + 1};
- std::vector<string> attributionTags = {"App1", "GMSCoreModule1"};
-
- auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids,
- attributionTags, "ReadEmail");
- auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids,
- attributionTags, "ReadEmail");
- auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000,
- attributionUids, attributionTags, "ReadDoc");
-
- auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid);
- auto moveToForegroundEvent1 =
- CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid);
-
- auto moveToBackgroundEvent2 =
- CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid);
- auto moveToForegroundEvent2 =
- CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid);
-
- /*
- bucket #1 bucket #2
-
-
- | | | | | | | | | | (crashEvents)
- |-------------------------------------|-----------------------------------|---------
-
- | | (MoveToBkground)
-
- | | (MoveToForeground)
-
- | | (SyncIsOn)
- | (SyncIsOff)
- | | (ScreenIsOn)
- | (ScreenIsOff)
- */
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(std::move(crashEvent1));
- events.push_back(std::move(crashEvent2));
- events.push_back(std::move(crashEvent3));
- events.push_back(std::move(crashEvent4));
- events.push_back(std::move(crashEvent5));
- events.push_back(std::move(crashEvent6));
- events.push_back(std::move(crashEvent7));
- events.push_back(std::move(crashEvent8));
- events.push_back(std::move(crashEvent9));
- events.push_back(std::move(crashEvent10));
- events.push_back(std::move(screenTurnedOnEvent));
- events.push_back(std::move(screenTurnedOffEvent));
- events.push_back(std::move(screenTurnedOnEvent2));
- events.push_back(std::move(syncOnEvent1));
- events.push_back(std::move(syncOffEvent1));
- events.push_back(std::move(syncOnEvent2));
- events.push_back(std::move(moveToBackgroundEvent1));
- events.push_back(std::move(moveToForegroundEvent1));
- events.push_back(std::move(moveToBackgroundEvent2));
- events.push_back(std::move(moveToForegroundEvent2));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
- auto data = reports.reports(0).metrics(0).count_metrics().data(0);
- // Validate dimension value.
- EXPECT_EQ(data.dimensions_in_what().field(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- // Uid field.
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
-}
-
-TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks2) {
- auto config = CreateStatsdConfig();
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- int appUid = 123;
- auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid);
- auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid);
- auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid);
-
- auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid);
- auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid);
- auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid);
-
- auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid);
- auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid);
-
- auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid);
- auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid);
-
- auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- auto screenTurnedOnEvent2 =
- CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-
- std::vector<int> attributionUids = {appUid, appUid + 1};
- std::vector<string> attributionTags = {"App1", "GMSCoreModule1"};
-
- auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids,
- attributionTags, "ReadEmail");
- auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids,
- attributionTags, "ReadEmail");
- auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000,
- attributionUids, attributionTags, "ReadDoc");
-
- auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid);
- auto moveToForegroundEvent1 =
- CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid);
-
- auto moveToBackgroundEvent2 =
- CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid);
- auto moveToForegroundEvent2 =
- CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid);
-
- /*
- bucket #1 bucket #2
-
-
- | | | | | | | | | | (crashEvents)
- |-------------------------------------|-----------------------------------|---------
-
- | | (MoveToBkground)
-
- | | (MoveToForeground)
-
- | | (SyncIsOn)
- | (SyncIsOff)
- | | (ScreenIsOn)
- | (ScreenIsOff)
- */
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(std::move(crashEvent1));
- events.push_back(std::move(crashEvent2));
- events.push_back(std::move(crashEvent3));
- events.push_back(std::move(crashEvent4));
- events.push_back(std::move(crashEvent5));
- events.push_back(std::move(crashEvent6));
- events.push_back(std::move(crashEvent7));
- events.push_back(std::move(crashEvent8));
- events.push_back(std::move(crashEvent9));
- events.push_back(std::move(crashEvent10));
- events.push_back(std::move(screenTurnedOnEvent));
- events.push_back(std::move(screenTurnedOffEvent));
- events.push_back(std::move(screenTurnedOnEvent2));
- events.push_back(std::move(syncOnEvent1));
- events.push_back(std::move(syncOffEvent1));
- events.push_back(std::move(syncOnEvent2));
- events.push_back(std::move(moveToBackgroundEvent1));
- events.push_back(std::move(moveToForegroundEvent1));
- events.push_back(std::move(moveToBackgroundEvent2));
- events.push_back(std::move(moveToForegroundEvent2));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
-
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
- EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
- auto data = reports.reports(0).metrics(0).count_metrics().data(0);
- // Validate dimension value.
- EXPECT_EQ(data.dimensions_in_what().field(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- // Uid field.
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
deleted file mode 100644
index c03b9254f70d..000000000000
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ /dev/null
@@ -1,433 +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.
-
-#include <android/binder_ibinder.h>
-#include <android/binder_interface_utils.h>
-#include <gtest/gtest.h>
-
-#include <vector>
-
-#include "src/StatsLogProcessor.h"
-#include "src/StatsService.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-using::ndk::SharedRefBase;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-namespace {
-const string kApp1 = "app1.sharing.1";
-const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs.
-const int kCallingUid = 0; // Randomly chosen
-
-void SendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) {
- string str;
- config.SerializeToString(&str);
- std::vector<uint8_t> configAsVec(str.begin(), str.end());
- service->addConfiguration(kConfigKey, configAsVec, kCallingUid);
-}
-
-ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
- bool include_current = false) {
- vector<uint8_t> output;
- ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey);
- processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
- true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output);
- ConfigMetricsReportList reports;
- reports.ParseFromArray(output.data(), output.size());
- EXPECT_EQ(1, reports.reports_size());
- return reports.reports(kCallingUid);
-}
-
-StatsdConfig MakeConfig() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto appCrashMatcher = CreateProcessCrashAtomMatcher();
- *config.add_atom_matcher() = appCrashMatcher;
- auto countMetric = config.add_count_metric();
- countMetric->set_id(StringToId("AppCrashes"));
- countMetric->set_what(appCrashMatcher.id());
- countMetric->set_bucket(FIVE_MINUTES);
- return config;
-}
-
-StatsdConfig MakeValueMetricConfig(int64_t minTime) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
-
- auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
- *config.add_atom_matcher() = pulledAtomMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto valueMetric = config.add_value_metric();
- valueMetric->set_id(123456);
- valueMetric->set_what(pulledAtomMatcher.id());
- *valueMetric->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- *valueMetric->mutable_dimensions_in_what() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
- valueMetric->set_bucket(FIVE_MINUTES);
- valueMetric->set_min_bucket_size_nanos(minTime);
- valueMetric->set_use_absolute_value_on_reset(true);
- valueMetric->set_skip_zero_diff_output(false);
- return config;
-}
-
-StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
-
- auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
- *config.add_atom_matcher() = pulledAtomMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto gaugeMetric = config.add_gauge_metric();
- gaugeMetric->set_id(123456);
- gaugeMetric->set_what(pulledAtomMatcher.id());
- gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
- *gaugeMetric->mutable_dimensions_in_what() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
- gaugeMetric->set_bucket(FIVE_MINUTES);
- gaugeMetric->set_min_bucket_size_nanos(minTime);
- return config;
-}
-} // anonymous namespace
-
-TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- SendConfig(service, MakeConfig());
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 2, 100).get());
-
- ConfigMetricsReport report = GetReports(service->mProcessor, start + 3);
- // Expect no metrics since the bucket has not finished yet.
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(0, report.metrics(0).count_metrics().data_size());
-}
-
-TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- SendConfig(service, MakeConfig());
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- // Force the uidmap to update at timestamp 2.
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
- // This is a new installation, so there shouldn't be a split (should be same as the without
- // split case).
- service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
- // Goes into the second bucket.
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
-
- ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(0, report.metrics(0).count_metrics().data_size());
-}
-
-TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- SendConfig(service, MakeConfig());
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
- service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
- {String16("")});
-
- // Force the uidmap to update at timestamp 2.
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
- service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
- // Goes into the second bucket.
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
-
- ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
- backfillStartEndTimestamp(&report);
-
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
- ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
- EXPECT_TRUE(report.metrics(0)
- .count_metrics()
- .data(0)
- .bucket_info(0)
- .has_start_bucket_elapsed_nanos());
- EXPECT_TRUE(report.metrics(0)
- .count_metrics()
- .data(0)
- .bucket_info(0)
- .has_end_bucket_elapsed_nanos());
- EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
-}
-
-TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- SendConfig(service, MakeConfig());
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
- service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
- {String16("")});
-
- // Force the uidmap to update at timestamp 2.
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
- service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
- // Goes into the second bucket.
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
-
- ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
- backfillStartEndTimestamp(&report);
-
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
- ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
- EXPECT_TRUE(report.metrics(0)
- .count_metrics()
- .data(0)
- .bucket_info(0)
- .has_start_bucket_elapsed_nanos());
- EXPECT_TRUE(report.metrics(0)
- .count_metrics()
- .data(0)
- .bucket_info(0)
- .has_end_bucket_elapsed_nanos());
- EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
-}
-
-TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- SendConfig(service, MakeConfig());
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- // Goes into the first bucket
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + NS_PER_SEC, 100).get());
- int64_t bootCompleteTimeNs = start + 2 * NS_PER_SEC;
- service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
- // Goes into the second bucket.
- service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3 * NS_PER_SEC, 100).get());
-
- ConfigMetricsReport report = GetReports(service->mProcessor, start + 4 * NS_PER_SEC);
- backfillStartEndTimestamp(&report);
-
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
- ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
- EXPECT_TRUE(report.metrics(0)
- .count_metrics()
- .data(0)
- .bucket_info(0)
- .has_start_bucket_elapsed_nanos());
- EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)),
- report.metrics(0).count_metrics().data(0).bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
-}
-
-TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- service->mPullerManager->RegisterPullAtomCallback(
- /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- SharedRefBase::make<FakeSubsystemSleepCallback>());
- // Partial buckets don't occur when app is first installed.
- service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
- SendConfig(service, MakeValueMetricConfig(0));
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- int64_t appUpgradeTimeNs = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC;
- service->mUidMap->updateApp(appUpgradeTimeNs, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
-
- ConfigMetricsReport report =
- GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
- backfillStartEndTimestamp(&report);
-
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(0, report.metrics(0).value_metrics().skipped_size());
-
- // The fake subsystem state sleep puller returns two atoms.
- ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
- ASSERT_EQ(2, report.metrics(0).value_metrics().data(0).bucket_info_size());
- EXPECT_EQ(MillisToNano(NanoToMillis(appUpgradeTimeNs)),
- report.metrics(0).value_metrics().data(0).bucket_info(1).end_bucket_elapsed_nanos());
-}
-
-TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- service->mPullerManager->RegisterPullAtomCallback(
- /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- SharedRefBase::make<FakeSubsystemSleepCallback>());
- // Partial buckets don't occur when app is first installed.
- service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
- SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC;
- service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
-
- ConfigMetricsReport report =
- GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
- backfillStartEndTimestamp(&report);
-
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size());
- EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
- // Can't test the start time since it will be based on the actual time when the pulling occurs.
- EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
- report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
-
- ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
- ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size());
-}
-
-TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- // Initial pull will fail since puller is not registered.
- SendConfig(service, MakeValueMetricConfig(0));
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- service->mPullerManager->RegisterPullAtomCallback(
- /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- SharedRefBase::make<FakeSubsystemSleepCallback>());
-
- int64_t bootCompleteTimeNs = start + NS_PER_SEC;
- service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
-
- service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-
- ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
- backfillStartEndTimestamp(&report);
-
- // First bucket is dropped due to the initial pull failing
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size());
- EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)),
- report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
-
- // The fake subsystem state sleep puller returns two atoms.
- ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
- ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size());
- EXPECT_EQ(
- MillisToNano(NanoToMillis(bootCompleteTimeNs)),
- report.metrics(0).value_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos());
-}
-
-TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- service->mPullerManager->RegisterPullAtomCallback(
- /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- SharedRefBase::make<FakeSubsystemSleepCallback>());
- // Partial buckets don't occur when app is first installed.
- service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
- SendConfig(service, MakeGaugeMetricConfig(0));
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
- String16("v2"), String16(""));
-
- ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
- backfillStartEndTimestamp(&report);
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
- // The fake subsystem state sleep puller returns two atoms.
- ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
- ASSERT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
-}
-
-TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- // Partial buckets don't occur when app is first installed.
- service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
- service->mPullerManager->RegisterPullAtomCallback(
- /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- SharedRefBase::make<FakeSubsystemSleepCallback>());
- SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
- service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
-
- ConfigMetricsReport report =
- GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
- backfillStartEndTimestamp(&report);
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
- // Can't test the start time since it will be based on the actual time when the pulling occurs.
- EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
- EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
- report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
- ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
- ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
-}
-
-TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) {
- shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
- // Initial pull will fail since puller hasn't been registered.
- SendConfig(service, MakeGaugeMetricConfig(0));
- int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
- // initialized with.
-
- service->mPullerManager->RegisterPullAtomCallback(
- /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
- SharedRefBase::make<FakeSubsystemSleepCallback>());
-
- int64_t bootCompleteTimeNs = start + NS_PER_SEC;
- service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
-
- service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-
- ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
- backfillStartEndTimestamp(&report);
-
- ASSERT_EQ(1, report.metrics_size());
- ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
- // The fake subsystem state sleep puller returns two atoms.
- ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
- // No data in the first bucket, so nothing is reported
- ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
- EXPECT_EQ(
- MillisToNano(NanoToMillis(bootCompleteTimeNs)),
- report.metrics(0).gauge_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
deleted file mode 100644
index 4d3928277527..000000000000
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ /dev/null
@@ -1,679 +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.
-
-#include <android/binder_interface_utils.h>
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-using ::ndk::SharedRefBase;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-const int64_t metricId = 123456;
-
-StatsdConfig CreateStatsdConfig(bool useCondition = true) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
- auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
- *config.add_atom_matcher() = pulledAtomMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- *config.add_predicate() = screenIsOffPredicate;
-
- auto valueMetric = config.add_value_metric();
- valueMetric->set_id(metricId);
- valueMetric->set_what(pulledAtomMatcher.id());
- if (useCondition) {
- valueMetric->set_condition(screenIsOffPredicate.id());
- }
- *valueMetric->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- *valueMetric->mutable_dimensions_in_what() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
- valueMetric->set_bucket(FIVE_MINUTES);
- valueMetric->set_use_absolute_value_on_reset(true);
- valueMetric->set_skip_zero_diff_output(false);
- valueMetric->set_max_pull_delay_sec(INT_MAX);
- return config;
-}
-
-StatsdConfig CreateStatsdConfigWithStates() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
-
- auto pulledAtomMatcher = CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
- *config.add_atom_matcher() = pulledAtomMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateBatteryStateNoneMatcher();
- *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
-
- auto screenOnPredicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = screenOnPredicate;
-
- auto screenOffPredicate = CreateScreenIsOffPredicate();
- *config.add_predicate() = screenOffPredicate;
-
- auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate();
- *config.add_predicate() = deviceUnpluggedPredicate;
-
- auto screenOnOnBatteryPredicate = config.add_predicate();
- screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate"));
- screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate);
- addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate);
-
- auto screenOffOnBatteryPredicate = config.add_predicate();
- screenOffOnBatteryPredicate->set_id(StringToId("ScreenOffOnBattery"));
- screenOffOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenOffPredicate, screenOffOnBatteryPredicate);
- addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOffOnBatteryPredicate);
-
- const State screenState =
- CreateScreenStateWithSimpleOnOffMap(/*screen on id=*/321, /*screen off id=*/123);
- *config.add_state() = screenState;
-
- // ValueMetricSubsystemSleepWhileScreenOnOnBattery
- auto valueMetric1 = config.add_value_metric();
- valueMetric1->set_id(metricId);
- valueMetric1->set_what(pulledAtomMatcher.id());
- valueMetric1->set_condition(screenOnOnBatteryPredicate->id());
- *valueMetric1->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- valueMetric1->set_bucket(FIVE_MINUTES);
- valueMetric1->set_use_absolute_value_on_reset(true);
- valueMetric1->set_skip_zero_diff_output(false);
- valueMetric1->set_max_pull_delay_sec(INT_MAX);
-
- // ValueMetricSubsystemSleepWhileScreenOffOnBattery
- ValueMetric* valueMetric2 = config.add_value_metric();
- valueMetric2->set_id(StringToId("ValueMetricSubsystemSleepWhileScreenOffOnBattery"));
- valueMetric2->set_what(pulledAtomMatcher.id());
- valueMetric2->set_condition(screenOffOnBatteryPredicate->id());
- *valueMetric2->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- valueMetric2->set_bucket(FIVE_MINUTES);
- valueMetric2->set_use_absolute_value_on_reset(true);
- valueMetric2->set_skip_zero_diff_output(false);
- valueMetric2->set_max_pull_delay_sec(INT_MAX);
-
- // ValueMetricSubsystemSleepWhileOnBatterySliceScreen
- ValueMetric* valueMetric3 = config.add_value_metric();
- valueMetric3->set_id(StringToId("ValueMetricSubsystemSleepWhileOnBatterySliceScreen"));
- valueMetric3->set_what(pulledAtomMatcher.id());
- valueMetric3->set_condition(deviceUnpluggedPredicate.id());
- *valueMetric3->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- valueMetric3->add_slice_by_state(screenState.id());
- valueMetric3->set_bucket(FIVE_MINUTES);
- valueMetric3->set_use_absolute_value_on_reset(true);
- valueMetric3->set_skip_zero_diff_output(false);
- valueMetric3->set_max_pull_delay_sec(INT_MAX);
- return config;
-}
-
-} // namespace
-
-/**
- * Tests the initial condition and condition after the first log events for
- * value metrics with either a combination condition or simple condition.
- *
- * Metrics should be initialized with condition kUnknown (given that the
- * predicate is using the default InitialValue of UNKNOWN). The condition should
- * be updated to either kFalse or kTrue if a condition event is logged for all
- * children conditions.
- */
-TEST(ValueMetricE2eTest, TestInitialConditionChanges) {
- StatsdConfig config = CreateStatsdConfigWithStates();
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- int32_t tagId = util::SUBSYSTEM_SLEEP_STATE;
- auto processor =
- CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(), tagId);
-
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(3, metricsManager->mAllMetricProducers.size());
-
- // Combination condition metric - screen on and device unplugged
- sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0];
- // Simple condition metric - device unplugged
- sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[2];
-
- EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
-
- auto screenOnEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 30, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
- EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
-
- auto screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 40, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
- EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
-
- auto pluggedUsbEvent = CreateBatteryStateChangedEvent(
- configAddedTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
- processor->OnLogEvent(pluggedUsbEvent.get());
- EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition);
-
- auto pluggedNoneEvent = CreateBatteryStateChangedEvent(
- configAddedTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
- processor->OnLogEvent(pluggedNoneEvent.get());
- EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition);
- EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition);
-}
-
-TEST(ValueMetricE2eTest, TestPulledEvents) {
- auto config = CreateStatsdConfig();
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(),
- util::SUBSYSTEM_SLEEP_STATE);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
-
- // When creating the config, the value metric producer should register the alarm at the
- // end of the current bucket.
- ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
- EXPECT_EQ(bucketSizeNs,
- processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
- int64_t& expectedPullTimeNs =
- processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
-
- auto screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- auto screenOnEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- // Pulling alarm arrives on time and reset the sequential pulling alarm.
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
-
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
-
- screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 2 * bucketSizeNs + 15,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
-
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
-
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 11,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
-
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::ValueMetricDataWrapper valueMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
- ASSERT_GT((int)valueMetrics.data_size(), 1);
-
- auto data = valueMetrics.data(0);
- EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- // We have 4 buckets, the first one was incomplete since the condition was unknown.
- ASSERT_EQ(4, data.bucket_info_size());
-
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- ASSERT_EQ(1, data.bucket_info(0).values_size());
-
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- ASSERT_EQ(1, data.bucket_info(1).values_size());
-
- EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- ASSERT_EQ(1, data.bucket_info(2).values_size());
-
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
- ASSERT_EQ(1, data.bucket_info(3).values_size());
-}
-
-TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
- auto config = CreateStatsdConfig();
- int64_t baseTimeNs = getElapsedRealtimeNs();
- // 10 mins == 2 bucket durations.
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(),
- util::SUBSYSTEM_SLEEP_STATE);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
-
- // When creating the config, the value metric producer should register the alarm at the
- // end of the current bucket.
- ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
- EXPECT_EQ(bucketSizeNs,
- processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
- int64_t& expectedPullTimeNs =
- processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
-
- // Screen off/on/off events.
- auto screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- auto screenOnEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- screenOffEvent =
- CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- // Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the
- // future, data will be skipped.
- processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
-
- // This screen state change will start a new bucket.
- screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 65,
- android::view::DISPLAY_STATE_ON);
- processor->OnLogEvent(screenOnEvent.get());
-
- // The alarm is delayed but we already created a bucket thanks to the screen state condition.
- // This bucket does not have to be skipped since the alarm arrives in time for the next bucket.
- processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
-
- screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 6 * bucketSizeNs + 31,
- android::view::DISPLAY_STATE_OFF);
- processor->OnLogEvent(screenOffEvent.get());
-
- processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs);
-
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::ValueMetricDataWrapper valueMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
- ASSERT_GT((int)valueMetrics.data_size(), 1);
-
- auto data = valueMetrics.data(0);
- EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- ASSERT_EQ(3, data.bucket_info_size());
-
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- ASSERT_EQ(1, data.bucket_info(0).values_size());
-
- EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- ASSERT_EQ(1, data.bucket_info(1).values_size());
-
- EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- ASSERT_EQ(1, data.bucket_info(2).values_size());
-}
-
-TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
- auto config = CreateStatsdConfig(false);
- int64_t baseTimeNs = getElapsedRealtimeNs();
- int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
-
- auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
- *config.add_atom_matcher() = batterySaverStartMatcher;
- const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
- auto metric_activation = config.add_metric_activation();
- metric_activation->set_metric_id(metricId);
- metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
- auto event_activation = metric_activation->add_event_activation();
- event_activation->set_atom_matcher_id(batterySaverStartMatcher.id());
- event_activation->set_ttl_seconds(ttlNs / 1000000000);
-
- ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
- SharedRefBase::make<FakeSubsystemSleepCallback>(),
- util::SUBSYSTEM_SLEEP_STATE);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- processor->mPullerManager->ForceClearPullerCache();
-
- int startBucketNum = processor->mMetricsManagers.begin()
- ->second->mAllMetricProducers[0]
- ->getCurrentBucketNum();
- EXPECT_GT(startBucketNum, (int64_t)0);
- EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- // When creating the config, the value metric producer should register the alarm at the
- // end of the current bucket.
- ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
- EXPECT_EQ(bucketSizeNs,
- processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
- int64_t& expectedPullTimeNs =
- processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
-
- // Pulling alarm arrives on time and reset the sequential pulling alarm.
- processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns.
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
- EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- // Activate the metric. A pull occurs here
- const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
- auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
- processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns.
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs);
-
- processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns.
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
-
- // Create random event to deactivate metric.
- auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
- processor->OnLogEvent(deactivationEvent.get());
- EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
-
- processor->informPullAlarmFired(expectedPullTimeNs + 3);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs);
-
- processor->informPullAlarmFired(expectedPullTimeNs + 4);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(1, reports.reports_size());
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- StatsLogReport::ValueMetricDataWrapper valueMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
- ASSERT_GT((int)valueMetrics.data_size(), 0);
-
- auto data = valueMetrics.data(0);
- EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
- EXPECT_EQ(1 /* subsystem name field */,
- data.dimensions_in_what().value_tuple().dimensions_value(0).field());
- EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- // We have 2 full buckets, the two surrounding the activation are dropped.
- ASSERT_EQ(2, data.bucket_info_size());
-
- auto bucketInfo = data.bucket_info(0);
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- ASSERT_EQ(1, bucketInfo.values_size());
-
- bucketInfo = data.bucket_info(1);
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- ASSERT_EQ(1, bucketInfo.values_size());
-}
-
-/**
- * Test initialization of a simple value metric that is sliced by a state.
- *
- * ValueCpuUserTimePerScreenState
- */
-TEST(ValueMetricE2eTest, TestInitWithSlicedState) {
- // Create config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
- *config.add_atom_matcher() = pulledAtomMatcher;
-
- auto screenState = CreateScreenState();
- *config.add_state() = screenState;
-
- // Create value metric that slices by screen state without a map.
- int64_t metricId = 123456;
- auto valueMetric = config.add_value_metric();
- valueMetric->set_id(metricId);
- valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
- valueMetric->set_what(pulledAtomMatcher.id());
- *valueMetric->mutable_value_field() =
- CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
- valueMetric->add_slice_by_state(screenState.id());
- valueMetric->set_max_pull_delay_sec(INT_MAX);
-
- // Initialize StatsLogProcessor.
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- const uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000LL;
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- // Check that ValueMetricProducer was initialized correctly.
- ASSERT_EQ(1U, processor->mMetricsManagers.size());
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(1, metricsManager->mAllMetricProducers.size());
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
- ASSERT_EQ(0, metricProducer->mStateGroupMap.size());
-}
-
-/**
- * Test initialization of a value metric that is sliced by state and has
- * dimensions_in_what.
- *
- * ValueCpuUserTimePerUidPerUidProcessState
- */
-TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) {
- // Create config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto cpuTimePerUidMatcher =
- CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID);
- *config.add_atom_matcher() = cpuTimePerUidMatcher;
-
- auto uidProcessState = CreateUidProcessState();
- *config.add_state() = uidProcessState;
-
- // Create value metric that slices by screen state with a complete map.
- int64_t metricId = 123456;
- auto valueMetric = config.add_value_metric();
- valueMetric->set_id(metricId);
- valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
- valueMetric->set_what(cpuTimePerUidMatcher.id());
- *valueMetric->mutable_value_field() =
- CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
- *valueMetric->mutable_dimensions_in_what() =
- CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */});
- valueMetric->add_slice_by_state(uidProcessState.id());
- MetricStateLink* stateLink = valueMetric->add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
- valueMetric->set_max_pull_delay_sec(INT_MAX);
-
- // Initialize StatsLogProcessor.
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
-
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // Check that StateTrackers were initialized correctly.
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-
- // Check that ValueMetricProducer was initialized correctly.
- ASSERT_EQ(1U, processor->mMetricsManagers.size());
- sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
- EXPECT_TRUE(metricsManager->isConfigValid());
- ASSERT_EQ(1, metricsManager->mAllMetricProducers.size());
- sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
- ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
- ASSERT_EQ(0, metricProducer->mStateGroupMap.size());
-}
-
-/**
- * Test initialization of a value metric that is sliced by state and has
- * dimensions_in_what.
- *
- * ValueCpuUserTimePerUidPerUidProcessState
- */
-TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) {
- // Create config.
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-
- auto cpuTimePerUidMatcher =
- CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID);
- *config.add_atom_matcher() = cpuTimePerUidMatcher;
-
- auto uidProcessState = CreateUidProcessState();
- *config.add_state() = uidProcessState;
-
- // Create value metric that slices by screen state with a complete map.
- int64_t metricId = 123456;
- auto valueMetric = config.add_value_metric();
- valueMetric->set_id(metricId);
- valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
- valueMetric->set_what(cpuTimePerUidMatcher.id());
- *valueMetric->mutable_value_field() =
- CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
- valueMetric->add_slice_by_state(uidProcessState.id());
- MetricStateLink* stateLink = valueMetric->add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
- valueMetric->set_max_pull_delay_sec(INT_MAX);
-
- // Initialize StatsLogProcessor.
- const uint64_t bucketStartTimeNs = 10000000000; // 0:10
- int uid = 12345;
- int64_t cfgId = 98765;
- ConfigKey cfgKey(uid, cfgId);
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-
- // No StateTrackers are initialized.
- EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount());
-
- // Config initialization fails.
- ASSERT_EQ(0, processor->mMetricsManagers.size());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
deleted file mode 100644
index 52bc222e5fe2..000000000000
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ /dev/null
@@ -1,355 +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.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- *config.add_predicate() = screenIsOffPredicate;
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- // The predicate is dimensioning by any attribution node and both by uid and tag.
- FieldMatcher dimensions = CreateAttributionUidAndTagDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST});
- // Also slice by the wakelock tag
- dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock.
- *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto durationMetric = config.add_duration_metric();
- durationMetric->set_id(StringToId("WakelockDuration"));
- durationMetric->set_what(holdingWakelockPredicate.id());
- durationMetric->set_condition(screenIsOffPredicate.id());
- durationMetric->set_aggregation_type(aggregationType);
- // The metric is dimensioning by first attribution node and only by uid.
- *durationMetric->mutable_dimensions_in_what() =
- CreateAttributionUidDimensions(
- util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- durationMetric->set_bucket(FIVE_MINUTES);
- return config;
-}
-
-std::vector<int> attributionUids1 = {111, 222, 222};
-std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
-
-std::vector<int> attributionUids2 = {111, 222, 222};
-std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
-
-/*
-Events:
-Screen off is met from (200ns,1 min+500ns].
-Acquire event for wl1 from 2ns to 1min+2ns
-Acquire event for wl2 from 1min-10ns to 2min-15ns
-*/
-void FeedEvents(StatsdConfig config, sp<StatsLogProcessor> processor) {
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- auto screenTurnedOnEvent2 =
- CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 500,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-
- auto acquireEvent1 = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
- attributionTags1, "wl1");
- auto releaseEvent1 = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2,
- attributionUids1, attributionTags1, "wl1");
- auto acquireEvent2 = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 10,
- attributionUids2, attributionTags2, "wl2");
- auto releaseEvent2 = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 15,
- attributionUids2, attributionTags2, "wl2");
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(std::move(screenTurnedOnEvent));
- events.push_back(std::move(screenTurnedOffEvent));
- events.push_back(std::move(screenTurnedOnEvent2));
- events.push_back(std::move(acquireEvent1));
- events.push_back(std::move(acquireEvent2));
- events.push_back(std::move(releaseEvent1));
- events.push_back(std::move(releaseEvent2));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-}
-
-} // namespace
-
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1) {
- ConfigKey cfgKey;
- auto config = CreateStatsdConfig(DurationMetric::SUM);
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- FeedEvents(config, processor);
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- // Only 1 dimension output. The tag dimension in the predicate has been aggregated.
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
-
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
- // Validate dimension value.
- ValidateAttributionUidDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 111);
- // Validate bucket info.
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
- data = reports.reports(0).metrics(0).duration_metrics().data(0);
- // The wakelock holding interval starts from the screen off event and to the end of the 1st
- // bucket.
- EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
-}
-
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2) {
- ConfigKey cfgKey;
- auto config = CreateStatsdConfig(DurationMetric::SUM);
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- FeedEvents(config, processor);
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- // Dump the report after the end of 2nd bucket.
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
- // Validate dimension value.
- ValidateAttributionUidDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 111);
- // Two output buckets.
- // The wakelock holding interval in the 1st bucket starts from the screen off event and to
- // the end of the 1st bucket.
- EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(),
- bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200));
- // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
- // ends at the second screen on event.
- EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
-}
-
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) {
- ConfigKey cfgKey;
- auto config = CreateStatsdConfig(DurationMetric::SUM);
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- FeedEvents(config, processor);
- vector<uint8_t> buffer;
- ConfigMetricsReportList reports;
-
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(
- CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
- attributionUids1, attributionTags1, "wl3"));
- events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100,
- attributionUids1, attributionTags1, "wl3"));
- sortLogEventsByTimestamp(&events);
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
- ValidateAttributionUidDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 111);
- // The last wakelock holding spans 4 buckets.
- EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
- EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
- EXPECT_EQ((unsigned long long)data.bucket_info(4).duration_nanos(), bucketSizeNs);
- EXPECT_EQ((unsigned long long)data.bucket_info(5).duration_nanos(), 100UL);
-}
-
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration1) {
- ConfigKey cfgKey;
- auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- FeedEvents(config, processor);
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
-
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- ASSERT_EQ(reports.reports_size(), 1);
-
- // When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as
- // one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by
- // itself.
- ASSERT_EQ(1, reports.reports(0).metrics_size());
- ASSERT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size());
-}
-
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) {
- ConfigKey cfgKey;
- auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- FeedEvents(config, processor);
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- // Dump the report after the end of 2nd bucket. One dimension with one bucket.
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
- // Validate dimension value.
- ValidateAttributionUidDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 111);
- // The max is acquire event for wl1 to screen off start.
- EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200);
-}
-
-TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration3) {
- ConfigKey cfgKey;
- auto config = CreateStatsdConfig(DurationMetric::MAX_SPARSE);
- uint64_t bucketStartTimeNs = 10000000000;
- uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- FeedEvents(config, processor);
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
-
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(
- CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF));
- events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
- attributionUids1, attributionTags1, "wl3"));
- events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100,
- attributionUids1, attributionTags1, "wl3"));
- sortLogEventsByTimestamp(&events);
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP,
- FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
- ASSERT_EQ(reports.reports_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
- ValidateAttributionUidDimension(data.dimensions_in_what(),
- util::WAKELOCK_STATE_CHANGED, 111);
- // The last wakelock holding spans 4 buckets.
- EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs);
- EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + 5 * bucketSizeNs);
- EXPECT_EQ((unsigned long long)data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 6 * bucketSizeNs);
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
deleted file mode 100644
index 85a60886349e..000000000000
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-// 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 "src/external/StatsCallbackPuller.h"
-
-#include <aidl/android/os/BnPullAtomCallback.h>
-#include <aidl/android/os/IPullAtomResultReceiver.h>
-#include <aidl/android/util/StatsEventParcel.h>
-#include <android/binder_interface_utils.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include <chrono>
-#include <thread>
-#include <vector>
-
-#include "../metrics/metrics_test_helper.h"
-#include "src/stats_log_util.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using namespace testing;
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::os::BnPullAtomCallback;
-using aidl::android::os::IPullAtomResultReceiver;
-using aidl::android::util::StatsEventParcel;
-using ::ndk::SharedRefBase;
-using std::make_shared;
-using std::shared_ptr;
-using std::vector;
-using std::this_thread::sleep_for;
-using testing::Contains;
-
-namespace {
-int pullTagId = -12;
-bool pullSuccess;
-vector<int64_t> values;
-int64_t pullDelayNs;
-int64_t pullTimeoutNs;
-int64_t pullCoolDownNs;
-std::thread pullThread;
-
-AStatsEvent* createSimpleEvent(int64_t value) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, pullTagId);
- AStatsEvent_writeInt64(event, value);
- AStatsEvent_build(event);
- return event;
-}
-
-void executePull(const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
- // Convert stats_events into StatsEventParcels.
- vector<StatsEventParcel> parcels;
- for (int i = 0; i < values.size(); i++) {
- AStatsEvent* event = createSimpleEvent(values[i]);
- size_t size;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
-
- StatsEventParcel p;
- // vector.assign() creates a copy, but this is inevitable unless
- // stats_event.h/c uses a vector as opposed to a buffer.
- p.buffer.assign(buffer, buffer + size);
- parcels.push_back(std::move(p));
- AStatsEvent_release(event);
- }
-
- sleep_for(std::chrono::nanoseconds(pullDelayNs));
- resultReceiver->pullFinished(pullTagId, pullSuccess, parcels);
-}
-
-class FakePullAtomCallback : public BnPullAtomCallback {
-public:
- Status onPullAtom(int atomTag,
- const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
- // Force pull to happen in separate thread to simulate binder.
- pullThread = std::thread(executePull, resultReceiver);
- return Status::ok();
- }
-};
-
-class StatsCallbackPullerTest : public ::testing::Test {
-public:
- StatsCallbackPullerTest() {
- }
-
- void SetUp() override {
- pullSuccess = false;
- pullDelayNs = 0;
- values.clear();
- pullTimeoutNs = 10000000000LL; // 10 seconds.
- pullCoolDownNs = 1000000000; // 1 second.
- }
-
- void TearDown() override {
- if (pullThread.joinable()) {
- pullThread.join();
- }
- values.clear();
- }
-};
-} // Anonymous namespace.
-
-TEST_F(StatsCallbackPullerTest, PullSuccess) {
- shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
- int64_t value = 43;
- pullSuccess = true;
- values.push_back(value);
-
- StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- int64_t startTimeNs = getElapsedRealtimeNs();
- EXPECT_TRUE(puller.PullInternal(&dataHolder));
- int64_t endTimeNs = getElapsedRealtimeNs();
-
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs());
- EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value);
-}
-
-TEST_F(StatsCallbackPullerTest, PullFail) {
- shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
- pullSuccess = false;
- int64_t value = 1234;
- values.push_back(value);
-
- StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
-
- vector<shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.PullInternal(&dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-TEST_F(StatsCallbackPullerTest, PullTimeout) {
- shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
- pullSuccess = true;
- pullDelayNs = MillisToNano(5); // 5ms.
- pullTimeoutNs = 10000; // 10 microseconds.
- int64_t value = 4321;
- values.push_back(value);
-
- StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
-
- vector<shared_ptr<LogEvent>> dataHolder;
- int64_t startTimeNs = getElapsedRealtimeNs();
- // Returns true to let StatsPuller code evaluate the timeout.
- EXPECT_TRUE(puller.PullInternal(&dataHolder));
- int64_t endTimeNs = getElapsedRealtimeNs();
- int64_t actualPullDurationNs = endTimeNs - startTimeNs;
-
- // Pull should take at least the timeout amount of time, but should stop early because the delay
- // is bigger.
- EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
- EXPECT_GT(pullDelayNs, actualPullDurationNs);
- ASSERT_EQ(0, dataHolder.size());
-
- // Let the pull return and make sure that the dataHolder is not modified.
- pullThread.join();
- ASSERT_EQ(0, dataHolder.size());
-}
-
-// Register a puller and ensure that the timeout logic works.
-TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
- shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
- pullSuccess = true;
- pullDelayNs = MillisToNano(5); // 5 ms.
- pullTimeoutNs = 10000; // 10 microsseconds.
- int64_t value = 4321;
- int32_t uid = 123;
- values.push_back(value);
-
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
- vector<int32_t>(), cb);
- vector<shared_ptr<LogEvent>> dataHolder;
- int64_t startTimeNs = getElapsedRealtimeNs();
- // Returns false, since StatsPuller code will evaluate the timeout.
- EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, startTimeNs, &dataHolder));
- int64_t endTimeNs = getElapsedRealtimeNs();
- int64_t actualPullDurationNs = endTimeNs - startTimeNs;
-
- // Pull should take at least the timeout amount of time, but should stop early because the delay
- // is bigger. Make sure that the time is closer to the timeout, than to the intended delay.
- EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
- EXPECT_GT(pullDelayNs / 5, actualPullDurationNs);
- ASSERT_EQ(0, dataHolder.size());
-
- // Let the pull return and make sure that the dataHolder is not modified.
- pullThread.join();
- ASSERT_EQ(0, dataHolder.size());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/external/StatsPullerManager_test.cpp b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
deleted file mode 100644
index 0d539f477016..000000000000
--- a/cmds/statsd/tests/external/StatsPullerManager_test.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2020 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 "src/external/StatsPullerManager.h"
-
-#include <aidl/android/os/IPullAtomResultReceiver.h>
-#include <aidl/android/util/StatsEventParcel.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-using aidl::android::util::StatsEventParcel;
-using ::ndk::SharedRefBase;
-using std::make_shared;
-using std::shared_ptr;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-int pullTagId1 = 10101;
-int pullTagId2 = 10102;
-int uid1 = 9999;
-int uid2 = 8888;
-ConfigKey configKey(50, 12345);
-ConfigKey badConfigKey(60, 54321);
-int unregisteredUid = 98765;
-int64_t coolDownNs = NS_PER_SEC;
-int64_t timeoutNs = NS_PER_SEC / 2;
-
-AStatsEvent* createSimpleEvent(int32_t atomId, int32_t value) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_writeInt32(event, value);
- AStatsEvent_build(event);
- return event;
-}
-
-class FakePullAtomCallback : public BnPullAtomCallback {
-public:
- FakePullAtomCallback(int32_t uid) : mUid(uid){};
- Status onPullAtom(int atomTag,
- const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
- vector<StatsEventParcel> parcels;
- AStatsEvent* event = createSimpleEvent(atomTag, mUid);
- size_t size;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
-
- StatsEventParcel p;
- // vector.assign() creates a copy, but this is inevitable unless
- // stats_event.h/c uses a vector as opposed to a buffer.
- p.buffer.assign(buffer, buffer + size);
- parcels.push_back(std::move(p));
- AStatsEvent_release(event);
- resultReceiver->pullFinished(atomTag, /*success*/ true, parcels);
- return Status::ok();
- }
- int32_t mUid;
-};
-
-class FakePullUidProvider : public PullUidProvider {
-public:
- vector<int32_t> getPullAtomUids(int atomId) override {
- if (atomId == pullTagId1) {
- return {uid2, uid1};
- } else if (atomId == pullTagId2) {
- return {uid2};
- }
- return {};
- }
-};
-
-sp<StatsPullerManager> createPullerManagerAndRegister() {
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- shared_ptr<FakePullAtomCallback> cb1 = SharedRefBase::make<FakePullAtomCallback>(uid1);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
- shared_ptr<FakePullAtomCallback> cb2 = SharedRefBase::make<FakePullAtomCallback>(uid2);
- pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
- pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1);
- return pullerManager;
-}
-} // anonymous namespace
-
-TEST(StatsPullerManagerTest, TestPullInvalidUid) {
- sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
-
- vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data));
-}
-
-TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
- sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
-
- vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data));
- ASSERT_EQ(data.size(), 1);
- EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
- ASSERT_EQ(data[0]->getValues().size(), 1);
- EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid1);
-}
-
-TEST(StatsPullerManagerTest, TestPullInvalidConfigKey) {
- sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
- sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
- pullerManager->RegisterPullUidProvider(configKey, uidProvider);
-
- vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data));
-}
-
-TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
- sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
- sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
- pullerManager->RegisterPullUidProvider(configKey, uidProvider);
-
- vector<shared_ptr<LogEvent>> data;
- EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data));
- EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
- ASSERT_EQ(data[0]->getValues().size(), 1);
- EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
-}
-
-TEST(StatsPullerManagerTest, TestPullConfigKeyNoPullerWithUid) {
- sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
- sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
- pullerManager->RegisterPullUidProvider(configKey, uidProvider);
-
- vector<shared_ptr<LogEvent>> data;
- EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data));
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android \ No newline at end of file
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
deleted file mode 100644
index 55a90365e151..000000000000
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include <chrono>
-#include <thread>
-#include <vector>
-
-#include "../metrics/metrics_test_helper.h"
-#include "src/stats_log_util.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using namespace testing;
-using std::make_shared;
-using std::shared_ptr;
-using std::vector;
-using std::this_thread::sleep_for;
-using testing::Contains;
-
-namespace {
-int pullTagId = 10014;
-
-bool pullSuccess;
-vector<std::shared_ptr<LogEvent>> pullData;
-long pullDelayNs;
-
-class FakePuller : public StatsPuller {
-public:
- FakePuller()
- : StatsPuller(pullTagId, /*coolDownNs=*/MillisToNano(10), /*timeoutNs=*/MillisToNano(5)){};
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
- (*data) = pullData;
- sleep_for(std::chrono::nanoseconds(pullDelayNs));
- return pullSuccess;
- }
-};
-
-FakePuller puller;
-
-std::unique_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, pullTagId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
- AStatsEvent_writeInt64(statsEvent, value);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-class StatsPullerTest : public ::testing::Test {
-public:
- StatsPullerTest() {
- }
-
- void SetUp() override {
- puller.ForceClearCache();
- pullSuccess = false;
- pullDelayNs = 0;
- pullData.clear();
- }
-};
-
-} // Anonymous namespace.
-
-TEST_F(StatsPullerTest, PullSuccess) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = true;
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-
- sleep_for(std::chrono::milliseconds(11));
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
-
- pullSuccess = true;
-
- EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value);
-}
-
-TEST_F(StatsPullerTest, PullFailAfterSuccess) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = true;
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-
- sleep_for(std::chrono::milliseconds(11));
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
-
- pullSuccess = false;
- dataHolder.clear();
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-
- // Fails due to hitting the cool down.
- pullSuccess = true;
- dataHolder.clear();
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown
-TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
- pullData.push_back(createSimpleEvent(1111L, 33));
- pullSuccess = true;
- // timeout is 5ms
- pullDelayNs = MillisToNano(6);
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
- pullDelayNs = 0;
-
- pullSuccess = true;
- dataHolder.clear();
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-TEST_F(StatsPullerTest, PullFail) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = false;
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-TEST_F(StatsPullerTest, PullTakeTooLong) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = true;
- pullDelayNs = MillisToNano(6);
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-TEST_F(StatsPullerTest, PullTooFast) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = true;
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
-
- pullSuccess = true;
-
- dataHolder.clear();
- EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-}
-
-TEST_F(StatsPullerTest, PullFailsAndTooFast) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = false;
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
-
- pullSuccess = true;
-
- EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-TEST_F(StatsPullerTest, PullSameEventTime) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = true;
- int64_t eventTimeNs = getElapsedRealtimeNs();
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder));
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
-
- // Sleep to ensure the cool down expires.
- sleep_for(std::chrono::milliseconds(11));
- pullSuccess = true;
-
- dataHolder.clear();
- EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder));
- ASSERT_EQ(1, dataHolder.size());
- EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
- EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- ASSERT_EQ(1, dataHolder[0]->size());
- EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
-}
-
-// Test pull takes longer than timeout, 2nd pull happens at same event time
-TEST_F(StatsPullerTest, PullTakeTooLongAndPullSameEventTime) {
- pullData.push_back(createSimpleEvent(1111L, 33));
- pullSuccess = true;
- int64_t eventTimeNs = getElapsedRealtimeNs();
- // timeout is 5ms
- pullDelayNs = MillisToNano(6);
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-
- // Sleep to ensure the cool down expires. 6ms is taken by the delay, so only 5 is needed here.
- sleep_for(std::chrono::milliseconds(5));
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
- pullDelayNs = 0;
-
- pullSuccess = true;
- dataHolder.clear();
- EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-TEST_F(StatsPullerTest, PullFailsAndPullSameEventTime) {
- pullData.push_back(createSimpleEvent(1111L, 33));
-
- pullSuccess = false;
- int64_t eventTimeNs = getElapsedRealtimeNs();
-
- vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-
- // Sleep to ensure the cool down expires.
- sleep_for(std::chrono::milliseconds(11));
-
- pullData.clear();
- pullData.push_back(createSimpleEvent(2222L, 44));
-
- pullSuccess = true;
-
- EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
- ASSERT_EQ(0, dataHolder.size());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
deleted file mode 100644
index a21dc8717776..000000000000
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ /dev/null
@@ -1,408 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "external/puller_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "../metrics/metrics_test_helper.h"
-#include "FieldValue.h"
-#include "annotations.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using namespace testing;
-using std::shared_ptr;
-using std::vector;
-/*
- * Test merge isolated and host uid
- */
-namespace {
-const int uidAtomTagId = 100;
-const vector<int> additiveFields = {3};
-const int nonUidAtomTagId = 200;
-const int timestamp = 1234;
-const int isolatedUid1 = 30;
-const int isolatedUid2 = 40;
-const int isolatedNonAdditiveData = 32;
-const int isolatedAdditiveData = 31;
-const int hostUid = 20;
-const int hostNonAdditiveData = 22;
-const int hostAdditiveData = 21;
-const int attributionAtomTagId = 300;
-
-sp<MockUidMap> makeMockUidMap() {
- return makeMockUidMapForOneHost(hostUid, {isolatedUid1, isolatedUid2});
-}
-
-} // anonymous namespace
-
-TEST(PullerUtilTest, MergeNoDimension) {
- vector<shared_ptr<LogEvent>> data = {
- // 30->22->31
- makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData,
- isolatedAdditiveData),
-
- // 20->22->21
- makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
- hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
-
- ASSERT_EQ(1, (int)data.size());
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
-}
-
-TEST(PullerUtilTest, MergeWithDimension) {
- vector<shared_ptr<LogEvent>> data = {
- // 30->32->31
- makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 20->32->21
- makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
- hostAdditiveData),
-
- // 20->22->21
- makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
- hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
-
- ASSERT_EQ(2, (int)data.size());
-
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
-
- actualFieldValues = &data[1]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
-}
-
-TEST(PullerUtilTest, NoMergeHostUidOnly) {
- vector<shared_ptr<LogEvent>> data = {
- // 20->32->31
- makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 20->22->21
- makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
- hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
-
- ASSERT_EQ(2, (int)data.size());
-
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
-
- actualFieldValues = &data[1]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
-}
-
-TEST(PullerUtilTest, IsolatedUidOnly) {
- vector<shared_ptr<LogEvent>> data = {
- // 30->32->31
- makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 30->22->21
- makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData,
- hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
-
- ASSERT_EQ(2, (int)data.size());
-
- // 20->32->31
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
-
- // 20->22->21
- actualFieldValues = &data[1]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
-}
-
-TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUid) {
- vector<shared_ptr<LogEvent>> data = {
- // 30->32->31
- makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 31->32->21
- makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid2, isolatedNonAdditiveData,
- hostAdditiveData),
-
- // 20->32->21
- makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
- hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
-
- ASSERT_EQ(1, (int)data.size());
-
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(3, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData,
- actualFieldValues->at(2).mValue.int_value);
-}
-
-TEST(PullerUtilTest, NoNeedToMerge) {
- vector<shared_ptr<LogEvent>> data = {
- // 32->31
- CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 22->21
- CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, hostNonAdditiveData,
- hostAdditiveData),
-
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, nonUidAtomTagId, {} /*no additive fields*/);
-
- ASSERT_EQ(2, (int)data.size());
-
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(2, actualFieldValues->size());
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(1).mValue.int_value);
-
- actualFieldValues = &data[1]->getValues();
- ASSERT_EQ(2, actualFieldValues->size());
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ(hostAdditiveData, actualFieldValues->at(1).mValue.int_value);
-}
-
-TEST(PullerUtilTest, MergeNoDimensionAttributionChain) {
- vector<shared_ptr<LogEvent>> data = {
- // 30->tag1->400->tag2->22->31
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
- {"tag1", "tag2"}, hostNonAdditiveData, isolatedAdditiveData),
-
- // 20->tag1->400->tag2->22->21
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
- {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
-
- ASSERT_EQ(1, (int)data.size());
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
-}
-
-TEST(PullerUtilTest, MergeWithDimensionAttributionChain) {
- vector<shared_ptr<LogEvent>> data = {
- // 200->tag1->30->tag2->32->31
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, isolatedUid1},
- {"tag1", "tag2"}, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 200->tag1->20->tag2->32->21
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid},
- {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
-
- // 200->tag1->20->tag2->22->21
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid},
- {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
-
- ASSERT_EQ(2, (int)data.size());
-
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
-
- actualFieldValues = &data[1]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
-}
-
-TEST(PullerUtilTest, NoMergeHostUidOnlyAttributionChain) {
- vector<shared_ptr<LogEvent>> data = {
- // 20->tag1->400->tag2->32->31
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
- {"tag1", "tag2"}, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 20->tag1->400->tag2->22->21
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
- {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
-
- ASSERT_EQ(2, (int)data.size());
-
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
-
- actualFieldValues = &data[1]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
-}
-
-TEST(PullerUtilTest, IsolatedUidOnlyAttributionChain) {
- vector<shared_ptr<LogEvent>> data = {
- // 30->tag1->400->tag2->32->31
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
- {"tag1", "tag2"}, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 30->tag1->400->tag2->22->21
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
- {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
-
- ASSERT_EQ(2, (int)data.size());
-
- // 20->tag1->400->tag2->32->31
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
-
- // 20->tag1->400->tag2->22->21
- actualFieldValues = &data[1]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
-}
-
-TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUidAttributionChain) {
- vector<shared_ptr<LogEvent>> data = {
- // 30->tag1->400->tag2->32->31
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
- {"tag1", "tag2"}, isolatedNonAdditiveData,
- isolatedAdditiveData),
-
- // 31->tag1->400->tag2->32->21
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid2, 400},
- {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
-
- // 20->tag1->400->tag2->32->21
- makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
- {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
- };
-
- sp<MockUidMap> uidMap = makeMockUidMap();
- mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
-
- ASSERT_EQ(1, (int)data.size());
-
- const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
- ASSERT_EQ(6, actualFieldValues->size());
- EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
- EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
- EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
- EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
- EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
- EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData,
- actualFieldValues->at(5).mValue.int_value);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
deleted file mode 100644
index 428c46f8a0d2..000000000000
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ /dev/null
@@ -1,544 +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.
-
-#include "src/guardrail/StatsdStats.h"
-#include "statslog_statsdtest.h"
-#include "tests/statsd_test_util.h"
-
-#include <gtest/gtest.h>
-#include <vector>
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::vector;
-
-TEST(StatsdStatsTest, TestValidConfigAdd) {
- StatsdStats stats;
- ConfigKey key(0, 12345);
- const int metricsCount = 10;
- const int conditionsCount = 20;
- const int matchersCount = 30;
- const int alertsCount = 10;
- stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
- true /*valid config*/);
- vector<uint8_t> output;
- stats.dumpStats(&output, false /*reset stats*/);
-
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
- ASSERT_EQ(1, report.config_stats_size());
- const auto& configReport = report.config_stats(0);
- EXPECT_EQ(0, configReport.uid());
- EXPECT_EQ(12345, configReport.id());
- EXPECT_EQ(metricsCount, configReport.metric_count());
- EXPECT_EQ(conditionsCount, configReport.condition_count());
- EXPECT_EQ(matchersCount, configReport.matcher_count());
- EXPECT_EQ(alertsCount, configReport.alert_count());
- EXPECT_EQ(true, configReport.is_valid());
- EXPECT_FALSE(configReport.has_deletion_time_sec());
-}
-
-TEST(StatsdStatsTest, TestInvalidConfigAdd) {
- StatsdStats stats;
- ConfigKey key(0, 12345);
- const int metricsCount = 10;
- const int conditionsCount = 20;
- const int matchersCount = 30;
- const int alertsCount = 10;
- stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
- false /*bad config*/);
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
-
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
- ASSERT_EQ(1, report.config_stats_size());
- const auto& configReport = report.config_stats(0);
- // The invalid config should be put into icebox with a deletion time.
- EXPECT_TRUE(configReport.has_deletion_time_sec());
-}
-
-TEST(StatsdStatsTest, TestConfigRemove) {
- StatsdStats stats;
- ConfigKey key(0, 12345);
- const int metricsCount = 10;
- const int conditionsCount = 20;
- const int matchersCount = 30;
- const int alertsCount = 10;
- stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, {},
- true);
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
- ASSERT_EQ(1, report.config_stats_size());
- const auto& configReport = report.config_stats(0);
- EXPECT_FALSE(configReport.has_deletion_time_sec());
-
- stats.noteConfigRemoved(key);
- stats.dumpStats(&output, false);
- good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
- ASSERT_EQ(1, report.config_stats_size());
- const auto& configReport2 = report.config_stats(0);
- EXPECT_TRUE(configReport2.has_deletion_time_sec());
-}
-
-TEST(StatsdStatsTest, TestSubStats) {
- StatsdStats stats;
- ConfigKey key(0, 12345);
- stats.noteConfigReceived(key, 2, 3, 4, 5, {std::make_pair(123, 456)}, true);
-
- stats.noteMatcherMatched(key, StringToId("matcher1"));
- stats.noteMatcherMatched(key, StringToId("matcher1"));
- stats.noteMatcherMatched(key, StringToId("matcher2"));
-
- stats.noteConditionDimensionSize(key, StringToId("condition1"), 250);
- stats.noteConditionDimensionSize(key, StringToId("condition1"), 240);
-
- stats.noteMetricDimensionSize(key, StringToId("metric1"), 201);
- stats.noteMetricDimensionSize(key, StringToId("metric1"), 202);
-
- stats.noteAnomalyDeclared(key, StringToId("alert1"));
- stats.noteAnomalyDeclared(key, StringToId("alert1"));
- stats.noteAnomalyDeclared(key, StringToId("alert2"));
-
- // broadcast-> 2
- stats.noteBroadcastSent(key);
- stats.noteBroadcastSent(key);
-
- // data drop -> 1
- stats.noteDataDropped(key, 123);
-
- // dump report -> 3
- stats.noteMetricsReportSent(key, 0);
- stats.noteMetricsReportSent(key, 0);
- stats.noteMetricsReportSent(key, 0);
-
- // activation_time_sec -> 2
- stats.noteActiveStatusChanged(key, true);
- stats.noteActiveStatusChanged(key, true);
-
- // deactivation_time_sec -> 1
- stats.noteActiveStatusChanged(key, false);
-
- vector<uint8_t> output;
- stats.dumpStats(&output, true); // Dump and reset stats
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
- ASSERT_EQ(1, report.config_stats_size());
- const auto& configReport = report.config_stats(0);
- ASSERT_EQ(2, configReport.broadcast_sent_time_sec_size());
- ASSERT_EQ(1, configReport.data_drop_time_sec_size());
- ASSERT_EQ(1, configReport.data_drop_bytes_size());
- EXPECT_EQ(123, configReport.data_drop_bytes(0));
- ASSERT_EQ(3, configReport.dump_report_time_sec_size());
- ASSERT_EQ(3, configReport.dump_report_data_size_size());
- ASSERT_EQ(2, configReport.activation_time_sec_size());
- ASSERT_EQ(1, configReport.deactivation_time_sec_size());
- ASSERT_EQ(1, configReport.annotation_size());
- EXPECT_EQ(123, configReport.annotation(0).field_int64());
- EXPECT_EQ(456, configReport.annotation(0).field_int32());
-
- ASSERT_EQ(2, configReport.matcher_stats_size());
- // matcher1 is the first in the list
- if (configReport.matcher_stats(0).id() == StringToId("matcher1")) {
- EXPECT_EQ(2, configReport.matcher_stats(0).matched_times());
- EXPECT_EQ(1, configReport.matcher_stats(1).matched_times());
- EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(1).id());
- } else {
- // matcher1 is the second in the list.
- EXPECT_EQ(1, configReport.matcher_stats(0).matched_times());
- EXPECT_EQ(StringToId("matcher2"), configReport.matcher_stats(0).id());
-
- EXPECT_EQ(2, configReport.matcher_stats(1).matched_times());
- EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id());
- }
-
- ASSERT_EQ(2, configReport.alert_stats_size());
- bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1");
- EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id());
- EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times());
- EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id());
- EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times());
-
- ASSERT_EQ(1, configReport.condition_stats_size());
- EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id());
- EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts());
-
- ASSERT_EQ(1, configReport.metric_stats_size());
- EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id());
- EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts());
-
- // after resetting the stats, some new events come
- stats.noteMatcherMatched(key, StringToId("matcher99"));
- stats.noteConditionDimensionSize(key, StringToId("condition99"), 300);
- stats.noteMetricDimensionSize(key, StringToId("metric99tion99"), 270);
- stats.noteAnomalyDeclared(key, StringToId("alert99"));
-
- // now the config stats should only contain the stats about the new event.
- stats.dumpStats(&output, false);
- good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
- ASSERT_EQ(1, report.config_stats_size());
- const auto& configReport2 = report.config_stats(0);
- ASSERT_EQ(1, configReport2.matcher_stats_size());
- EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id());
- EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times());
-
- ASSERT_EQ(1, configReport2.condition_stats_size());
- EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id());
- EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts());
-
- ASSERT_EQ(1, configReport2.metric_stats_size());
- EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id());
- EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts());
-
- ASSERT_EQ(1, configReport2.alert_stats_size());
- EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id());
- EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times());
-}
-
-TEST(StatsdStatsTest, TestAtomLog) {
- StatsdStats stats;
- time_t now = time(nullptr);
- // old event, we get it from the stats buffer. should be ignored.
- stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, 1000);
-
- stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 1);
- stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 2);
- stats.noteAtomLogged(util::APP_CRASH_OCCURRED, now + 3);
-
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
-
- ASSERT_EQ(2, report.atom_stats_size());
- bool sensorAtomGood = false;
- bool dropboxAtomGood = false;
-
- for (const auto& atomStats : report.atom_stats()) {
- if (atomStats.tag() == util::SENSOR_STATE_CHANGED && atomStats.count() == 3) {
- sensorAtomGood = true;
- }
- if (atomStats.tag() == util::APP_CRASH_OCCURRED && atomStats.count() == 1) {
- dropboxAtomGood = true;
- }
- }
-
- EXPECT_TRUE(dropboxAtomGood);
- EXPECT_TRUE(sensorAtomGood);
-}
-
-TEST(StatsdStatsTest, TestNonPlatformAtomLog) {
- StatsdStats stats;
- time_t now = time(nullptr);
- int newAtom1 = StatsdStats::kMaxPushedAtomId + 1;
- int newAtom2 = StatsdStats::kMaxPushedAtomId + 2;
-
- stats.noteAtomLogged(newAtom1, now + 1);
- stats.noteAtomLogged(newAtom1, now + 2);
- stats.noteAtomLogged(newAtom2, now + 3);
-
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
-
- ASSERT_EQ(2, report.atom_stats_size());
- bool newAtom1Good = false;
- bool newAtom2Good = false;
-
- for (const auto& atomStats : report.atom_stats()) {
- if (atomStats.tag() == newAtom1 && atomStats.count() == 2) {
- newAtom1Good = true;
- }
- if (atomStats.tag() == newAtom2 && atomStats.count() == 1) {
- newAtom2Good = true;
- }
- }
-
- EXPECT_TRUE(newAtom1Good);
- EXPECT_TRUE(newAtom2Good);
-}
-
-TEST(StatsdStatsTest, TestPullAtomStats) {
- StatsdStats stats;
-
- stats.updateMinPullIntervalSec(util::DISK_SPACE, 3333L);
- stats.updateMinPullIntervalSec(util::DISK_SPACE, 2222L);
- stats.updateMinPullIntervalSec(util::DISK_SPACE, 4444L);
-
- stats.notePull(util::DISK_SPACE);
- stats.notePullTime(util::DISK_SPACE, 1111L);
- stats.notePullDelay(util::DISK_SPACE, 1111L);
- stats.notePull(util::DISK_SPACE);
- stats.notePullTime(util::DISK_SPACE, 3333L);
- stats.notePullDelay(util::DISK_SPACE, 3335L);
- stats.notePull(util::DISK_SPACE);
- stats.notePullFromCache(util::DISK_SPACE);
- stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
- stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, false);
- stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
- stats.notePullBinderCallFailed(util::DISK_SPACE);
- stats.notePullUidProviderNotFound(util::DISK_SPACE);
- stats.notePullerNotFound(util::DISK_SPACE);
- stats.notePullerNotFound(util::DISK_SPACE);
- stats.notePullTimeout(util::DISK_SPACE, 3000L, 6000L);
- stats.notePullTimeout(util::DISK_SPACE, 4000L, 7000L);
-
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
-
- ASSERT_EQ(1, report.pulled_atom_stats_size());
-
- EXPECT_EQ(util::DISK_SPACE, report.pulled_atom_stats(0).atom_id());
- EXPECT_EQ(3, report.pulled_atom_stats(0).total_pull());
- EXPECT_EQ(1, report.pulled_atom_stats(0).total_pull_from_cache());
- EXPECT_EQ(2222L, report.pulled_atom_stats(0).min_pull_interval_sec());
- EXPECT_EQ(2222L, report.pulled_atom_stats(0).average_pull_time_nanos());
- EXPECT_EQ(3333L, report.pulled_atom_stats(0).max_pull_time_nanos());
- EXPECT_EQ(2223L, report.pulled_atom_stats(0).average_pull_delay_nanos());
- EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
- EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count());
- EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count());
- EXPECT_EQ(1L, report.pulled_atom_stats(0).binder_call_failed());
- EXPECT_EQ(1L, report.pulled_atom_stats(0).failed_uid_provider_not_found());
- EXPECT_EQ(2L, report.pulled_atom_stats(0).puller_not_found());
- ASSERT_EQ(2, report.pulled_atom_stats(0).pull_atom_metadata_size());
- EXPECT_EQ(3000L, report.pulled_atom_stats(0).pull_atom_metadata(0).pull_timeout_uptime_millis());
- EXPECT_EQ(4000L, report.pulled_atom_stats(0).pull_atom_metadata(1).pull_timeout_uptime_millis());
- EXPECT_EQ(6000L, report.pulled_atom_stats(0).pull_atom_metadata(0)
- .pull_timeout_elapsed_millis());
- EXPECT_EQ(7000L, report.pulled_atom_stats(0).pull_atom_metadata(1)
- .pull_timeout_elapsed_millis());
-}
-
-TEST(StatsdStatsTest, TestAtomMetricsStats) {
- StatsdStats stats;
- time_t now = time(nullptr);
- // old event, we get it from the stats buffer. should be ignored.
- stats.noteBucketDropped(1000L);
-
- stats.noteBucketBoundaryDelayNs(1000L, -1L);
- stats.noteBucketBoundaryDelayNs(1000L, -10L);
- stats.noteBucketBoundaryDelayNs(1000L, 2L);
-
- stats.noteBucketBoundaryDelayNs(1001L, 1L);
-
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
-
- ASSERT_EQ(2, report.atom_metric_stats().size());
-
- auto atomStats = report.atom_metric_stats(0);
- EXPECT_EQ(1000L, atomStats.metric_id());
- EXPECT_EQ(1L, atomStats.bucket_dropped());
- EXPECT_EQ(-10L, atomStats.min_bucket_boundary_delay_ns());
- EXPECT_EQ(2L, atomStats.max_bucket_boundary_delay_ns());
-
- auto atomStats2 = report.atom_metric_stats(1);
- EXPECT_EQ(1001L, atomStats2.metric_id());
- EXPECT_EQ(0L, atomStats2.bucket_dropped());
- EXPECT_EQ(0L, atomStats2.min_bucket_boundary_delay_ns());
- EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns());
-}
-
-TEST(StatsdStatsTest, TestAnomalyMonitor) {
- StatsdStats stats;
- stats.noteRegisteredAnomalyAlarmChanged();
- stats.noteRegisteredAnomalyAlarmChanged();
-
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- bool good = report.ParseFromArray(&output[0], output.size());
- EXPECT_TRUE(good);
-
- EXPECT_EQ(2, report.anomaly_alarm_stats().alarms_registered());
-}
-
-TEST(StatsdStatsTest, TestTimestampThreshold) {
- StatsdStats stats;
- vector<int32_t> timestamps;
- for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
- timestamps.push_back(i);
- }
- ConfigKey key(0, 12345);
- stats.noteConfigReceived(key, 2, 3, 4, 5, {}, true);
-
- for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
- stats.noteDataDropped(key, timestamps[i]);
- stats.noteBroadcastSent(key, timestamps[i]);
- stats.noteMetricsReportSent(key, 0, timestamps[i]);
- stats.noteActiveStatusChanged(key, true, timestamps[i]);
- stats.noteActiveStatusChanged(key, false, timestamps[i]);
- }
-
- int32_t newTimestamp = 10000;
-
- // now it should trigger removing oldest timestamp
- stats.noteDataDropped(key, 123, 10000);
- stats.noteBroadcastSent(key, 10000);
- stats.noteMetricsReportSent(key, 0, 10000);
- stats.noteActiveStatusChanged(key, true, 10000);
- stats.noteActiveStatusChanged(key, false, 10000);
-
- EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end());
- const auto& configStats = stats.mConfigStats[key];
-
- size_t maxCount = StatsdStats::kMaxTimestampCount;
- ASSERT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
- ASSERT_EQ(maxCount, configStats->data_drop_time_sec.size());
- ASSERT_EQ(maxCount, configStats->dump_report_stats.size());
- ASSERT_EQ(maxCount, configStats->activation_time_sec.size());
- ASSERT_EQ(maxCount, configStats->deactivation_time_sec.size());
-
- // the oldest timestamp is the second timestamp in history
- EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
- EXPECT_EQ(1, configStats->data_drop_bytes.front());
- EXPECT_EQ(1, configStats->dump_report_stats.front().first);
- EXPECT_EQ(1, configStats->activation_time_sec.front());
- EXPECT_EQ(1, configStats->deactivation_time_sec.front());
-
- // the last timestamp is the newest timestamp.
- EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
- EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
- EXPECT_EQ(123, configStats->data_drop_bytes.back());
- EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first);
- EXPECT_EQ(newTimestamp, configStats->activation_time_sec.back());
- EXPECT_EQ(newTimestamp, configStats->deactivation_time_sec.back());
-}
-
-TEST(StatsdStatsTest, TestSystemServerCrash) {
- StatsdStats stats;
- vector<int32_t> timestamps;
- for (int i = 0; i < StatsdStats::kMaxSystemServerRestarts; i++) {
- timestamps.push_back(i);
- stats.noteSystemServerRestart(timestamps[i]);
- }
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
- const int maxCount = StatsdStats::kMaxSystemServerRestarts;
- ASSERT_EQ(maxCount, (int)report.system_restart_sec_size());
-
- stats.noteSystemServerRestart(StatsdStats::kMaxSystemServerRestarts + 1);
- output.clear();
- stats.dumpStats(&output, false);
- EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
- ASSERT_EQ(maxCount, (int)report.system_restart_sec_size());
- EXPECT_EQ(StatsdStats::kMaxSystemServerRestarts + 1, report.system_restart_sec(maxCount - 1));
-}
-
-TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit) {
- StatsdStats stats;
- int uid1 = 1;
- int uid2 = 2;
- stats.noteActivationBroadcastGuardrailHit(uid1, 10);
- stats.noteActivationBroadcastGuardrailHit(uid1, 20);
-
- // Test that we only keep 20 timestamps.
- for (int i = 0; i < 100; i++) {
- stats.noteActivationBroadcastGuardrailHit(uid2, i);
- }
-
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
-
- ASSERT_EQ(2, report.activation_guardrail_stats_size());
- bool uid1Good = false;
- bool uid2Good = false;
- for (const auto& guardrailTimes : report.activation_guardrail_stats()) {
- if (uid1 == guardrailTimes.uid()) {
- uid1Good = true;
- ASSERT_EQ(2, guardrailTimes.guardrail_met_sec_size());
- EXPECT_EQ(10, guardrailTimes.guardrail_met_sec(0));
- EXPECT_EQ(20, guardrailTimes.guardrail_met_sec(1));
- } else if (uid2 == guardrailTimes.uid()) {
- int maxCount = StatsdStats::kMaxTimestampCount;
- uid2Good = true;
- ASSERT_EQ(maxCount, guardrailTimes.guardrail_met_sec_size());
- for (int i = 0; i < maxCount; i++) {
- EXPECT_EQ(100 - maxCount + i, guardrailTimes.guardrail_met_sec(i));
- }
- } else {
- FAIL() << "Unexpected uid.";
- }
- }
- EXPECT_TRUE(uid1Good);
- EXPECT_TRUE(uid2Good);
-}
-
-TEST(StatsdStatsTest, TestAtomErrorStats) {
- StatsdStats stats;
-
- int pushAtomTag = 100;
- int pullAtomTag = 1000;
- int numErrors = 10;
-
- for (int i = 0; i < numErrors; i++) {
- // We must call noteAtomLogged as well because only those pushed atoms
- // that have been logged will have stats printed about them in the
- // proto.
- stats.noteAtomLogged(pushAtomTag, /*timeSec=*/0);
- stats.noteAtomError(pushAtomTag, /*pull=*/false);
-
- stats.noteAtomError(pullAtomTag, /*pull=*/true);
- }
-
- vector<uint8_t> output;
- stats.dumpStats(&output, false);
- StatsdStatsReport report;
- EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
-
- // Check error count = numErrors for push atom
- ASSERT_EQ(1, report.atom_stats_size());
- const auto& pushedAtomStats = report.atom_stats(0);
- EXPECT_EQ(pushAtomTag, pushedAtomStats.tag());
- EXPECT_EQ(numErrors, pushedAtomStats.error_count());
-
- // Check error count = numErrors for pull atom
- ASSERT_EQ(1, report.pulled_atom_stats_size());
- const auto& pulledAtomStats = report.pulled_atom_stats(0);
- EXPECT_EQ(pullAtomTag, pulledAtomStats.atom_id());
- EXPECT_EQ(numErrors, pulledAtomStats.atom_error_count());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
deleted file mode 100644
index 3a654565b4aa..000000000000
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ /dev/null
@@ -1,235 +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.
- */
-
-#include "src/anomaly/indexed_priority_queue.h"
-
-#include <gtest/gtest.h>
-
-using namespace android::os::statsd;
-
-/** struct for template in indexed_priority_queue */
-struct AATest : public RefBase {
- AATest(uint32_t val, std::string a, std::string b) : val(val), a(a), b(b) {
- }
-
- const int val;
- const std::string a;
- const std::string b;
-
- struct Smaller {
- bool operator()(const sp<const AATest> a, const sp<const AATest> b) const {
- return (a->val < b->val);
- }
- };
-};
-
-#ifdef __ANDROID__
-TEST(indexed_priority_queue, empty_and_size) {
- std::string emptyMetricId;
- std::string emptyDimensionId;
- indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
-
- ASSERT_EQ(0u, ipq.size());
- EXPECT_TRUE(ipq.empty());
-
- ipq.push(aa4);
- ASSERT_EQ(1u, ipq.size());
- EXPECT_FALSE(ipq.empty());
-
- ipq.push(aa8);
- ASSERT_EQ(2u, ipq.size());
- EXPECT_FALSE(ipq.empty());
-
- ipq.remove(aa4);
- ASSERT_EQ(1u, ipq.size());
- EXPECT_FALSE(ipq.empty());
-
- ipq.remove(aa8);
- ASSERT_EQ(0u, ipq.size());
- EXPECT_TRUE(ipq.empty());
-}
-
-TEST(indexed_priority_queue, top) {
- std::string emptyMetricId;
- std::string emptyDimensionId;
- indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa2 = new AATest{2, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa12 = new AATest{12, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa16 = new AATest{16, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa20 = new AATest{20, emptyMetricId, emptyDimensionId};
-
- EXPECT_EQ(ipq.top(), nullptr);
-
- // add 8, 4, 12
- ipq.push(aa8);
- EXPECT_EQ(ipq.top(), aa8);
-
- ipq.push(aa12);
- EXPECT_EQ(ipq.top(), aa8);
-
- ipq.push(aa4);
- EXPECT_EQ(ipq.top(), aa4);
-
- // remove 12, 4
- ipq.remove(aa12);
- EXPECT_EQ(ipq.top(), aa4);
-
- ipq.remove(aa4);
- EXPECT_EQ(ipq.top(), aa8);
-
- // add 16, 2, 20
- ipq.push(aa16);
- EXPECT_EQ(ipq.top(), aa8);
-
- ipq.push(aa2);
- EXPECT_EQ(ipq.top(), aa2);
-
- ipq.push(aa20);
- EXPECT_EQ(ipq.top(), aa2);
-
- // remove 2, 20, 16, 8
- ipq.remove(aa2);
- EXPECT_EQ(ipq.top(), aa8);
-
- ipq.remove(aa20);
- EXPECT_EQ(ipq.top(), aa8);
-
- ipq.remove(aa16);
- EXPECT_EQ(ipq.top(), aa8);
-
- ipq.remove(aa8);
- EXPECT_EQ(ipq.top(), nullptr);
-}
-
-TEST(indexed_priority_queue, push_same_aa) {
- std::string emptyMetricId;
- std::string emptyDimensionId;
- indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
-
- ipq.push(aa4_a);
- ASSERT_EQ(1u, ipq.size());
- EXPECT_TRUE(ipq.contains(aa4_a));
- EXPECT_FALSE(ipq.contains(aa4_b));
-
- ipq.push(aa4_a);
- ASSERT_EQ(1u, ipq.size());
- EXPECT_TRUE(ipq.contains(aa4_a));
- EXPECT_FALSE(ipq.contains(aa4_b));
-
- ipq.push(aa4_b);
- ASSERT_EQ(2u, ipq.size());
- EXPECT_TRUE(ipq.contains(aa4_a));
- EXPECT_TRUE(ipq.contains(aa4_b));
-}
-
-TEST(indexed_priority_queue, remove_nonexistant) {
- std::string emptyMetricId;
- std::string emptyDimensionId;
- indexed_priority_queue<AATest, AATest::Smaller> ipq;
- sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa5 = new AATest{5, emptyMetricId, emptyDimensionId};
-
- ipq.push(aa4);
- ipq.remove(aa5);
- ASSERT_EQ(1u, ipq.size());
- EXPECT_TRUE(ipq.contains(aa4));
- EXPECT_FALSE(ipq.contains(aa5));
-}
-
-TEST(indexed_priority_queue, remove_same_aa) {
- indexed_priority_queue<AATest, AATest::Smaller> ipq;
- std::string emptyMetricId;
- std::string emptyDimensionId;
- sp<const AATest> aa4_a = new AATest{4, emptyMetricId, emptyDimensionId};
- sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
-
- ipq.push(aa4_a);
- ipq.push(aa4_b);
- ASSERT_EQ(2u, ipq.size());
- EXPECT_TRUE(ipq.contains(aa4_a));
- EXPECT_TRUE(ipq.contains(aa4_b));
-
- ipq.remove(aa4_b);
- ASSERT_EQ(1u, ipq.size());
- EXPECT_TRUE(ipq.contains(aa4_a));
- EXPECT_FALSE(ipq.contains(aa4_b));
-
- ipq.remove(aa4_a);
- ASSERT_EQ(0u, ipq.size());
- EXPECT_FALSE(ipq.contains(aa4_a));
- EXPECT_FALSE(ipq.contains(aa4_b));
-}
-
-TEST(indexed_priority_queue, nulls) {
- indexed_priority_queue<AATest, AATest::Smaller> ipq;
-
- EXPECT_TRUE(ipq.empty());
- EXPECT_FALSE(ipq.contains(nullptr));
-
- ipq.push(nullptr);
- EXPECT_TRUE(ipq.empty());
- EXPECT_FALSE(ipq.contains(nullptr));
-
- ipq.remove(nullptr);
- EXPECT_TRUE(ipq.empty());
- EXPECT_FALSE(ipq.contains(nullptr));
-}
-
-TEST(indexed_priority_queue, pop) {
- indexed_priority_queue<AATest, AATest::Smaller> ipq;
- std::string emptyMetricId;
- std::string emptyDimensionId;
- sp<const AATest> a = new AATest{1, emptyMetricId, emptyDimensionId};
- sp<const AATest> b = new AATest{2, emptyMetricId, emptyDimensionId};
- sp<const AATest> c = new AATest{3, emptyMetricId, emptyDimensionId};
-
- ipq.push(c);
- ipq.push(b);
- ipq.push(a);
- ASSERT_EQ(3u, ipq.size());
-
- ipq.pop();
- ASSERT_EQ(2u, ipq.size());
- EXPECT_FALSE(ipq.contains(a));
- EXPECT_TRUE(ipq.contains(b));
- EXPECT_TRUE(ipq.contains(c));
-
- ipq.pop();
- ASSERT_EQ(1u, ipq.size());
- EXPECT_FALSE(ipq.contains(a));
- EXPECT_FALSE(ipq.contains(b));
- EXPECT_TRUE(ipq.contains(c));
-
- ipq.pop();
- ASSERT_EQ(0u, ipq.size());
- EXPECT_FALSE(ipq.contains(a));
- EXPECT_FALSE(ipq.contains(b));
- EXPECT_FALSE(ipq.contains(c));
- EXPECT_TRUE(ipq.empty());
-
- ipq.pop(); // pop an empty queue
- EXPECT_TRUE(ipq.empty());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
deleted file mode 100644
index a15f95bef358..000000000000
--- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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 "logd/LogEventQueue.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include <thread>
-
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using namespace android;
-using namespace testing;
-
-using std::unique_ptr;
-
-namespace {
-
-std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, 10);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-} // anonymous namespace
-
-#ifdef __ANDROID__
-TEST(LogEventQueue_test, TestGoodConsumer) {
- LogEventQueue queue(50);
- int64_t timeBaseNs = 100;
- std::thread writer([&queue, timeBaseNs] {
- for (int i = 0; i < 100; i++) {
- int64_t oldestEventNs;
- bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
- EXPECT_TRUE(success);
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
- }
- });
-
- std::thread reader([&queue, timeBaseNs] {
- for (int i = 0; i < 100; i++) {
- auto event = queue.waitPop();
- EXPECT_TRUE(event != nullptr);
- // All events are in right order.
- EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs());
- }
- });
-
- reader.join();
- writer.join();
-}
-
-TEST(LogEventQueue_test, TestSlowConsumer) {
- LogEventQueue queue(50);
- int64_t timeBaseNs = 100;
- std::thread writer([&queue, timeBaseNs] {
- int failure_count = 0;
- int64_t oldestEventNs;
- for (int i = 0; i < 100; i++) {
- bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
- if (!success) failure_count++;
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
- }
-
- // There is some remote chance that reader thread not get chance to run before writer thread
- // ends. That's why the following comparison is not "==".
- // There will be at least 45 events lost due to overflow.
- EXPECT_TRUE(failure_count >= 45);
- // The oldest event must be at least the 6th event.
- EXPECT_TRUE(oldestEventNs <= (100 + 5 * 1000));
- });
-
- std::thread reader([&queue, timeBaseNs] {
- // The consumer quickly processed 5 events, then it got stuck (not reading anymore).
- for (int i = 0; i < 5; i++) {
- auto event = queue.waitPop();
- EXPECT_TRUE(event != nullptr);
- // All events are in right order.
- EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs());
- }
- });
-
- reader.join();
- writer.join();
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/metadata_util_test.cpp b/cmds/statsd/tests/metadata_util_test.cpp
deleted file mode 100644
index 7707890cbd0c..000000000000
--- a/cmds/statsd/tests/metadata_util_test.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2020 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 "metadata_util.h"
-#include "tests/statsd_test_util.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-TEST(MetadataUtilTest, TestWriteAndReadMetricDimensionKey) {
- HashableDimensionKey dim;
- HashableDimensionKey dim2;
- int pos1[] = {1, 1, 1};
- int pos2[] = {1, 1, 2};
- int pos3[] = {1, 1, 3};
- int pos4[] = {2, 0, 0};
- Field field1(10, pos1, 2);
- Field field2(10, pos2, 2);
- Field field3(10, pos3, 2);
- Field field4(10, pos4, 0);
-
- Value value1((int32_t)10025);
- Value value2("tag");
- Value value3((int32_t)987654);
- Value value4((int32_t)99999);
-
- dim.addValue(FieldValue(field1, value1));
- dim.addValue(FieldValue(field2, value2));
- dim.addValue(FieldValue(field3, value3));
- dim.addValue(FieldValue(field4, value4));
-
- dim2.addValue(FieldValue(field1, value1));
- dim2.addValue(FieldValue(field2, value2));
-
- MetricDimensionKey dimKey(dim, dim2);
-
- metadata::MetricDimensionKey metadataDimKey;
- writeMetricDimensionKeyToMetadataDimensionKey(dimKey, &metadataDimKey);
-
- MetricDimensionKey loadedDimKey = loadMetricDimensionKeyFromProto(metadataDimKey);
-
- ASSERT_EQ(loadedDimKey, dimKey);
- ASSERT_EQ(std::hash<MetricDimensionKey>{}(loadedDimKey),
- std::hash<MetricDimensionKey>{}(dimKey));
-}
-
-} // 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
deleted file mode 100644
index 8e2864c6fba8..000000000000
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ /dev/null
@@ -1,477 +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.
-
-#include "src/metrics/CountMetricProducer.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <math.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "metrics_test_helper.h"
-#include "src/stats_log_util.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-
-namespace {
-const ConfigKey kConfigKey(0, 12345);
-const uint64_t protoHash = 0x1234567890;
-
-void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeString(statsEvent, uid.c_str());
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-} // namespace
-
-// Setup for parameterized tests.
-class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
-
-INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket,
- CountMetricProducerTest_PartialBucket,
- testing::Values(APP_UPGRADE, BOOT_COMPLETE));
-
-TEST(CountMetricProducerTest, TestFirstBucket) {
- CountMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
- EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(10, countProducer.mCurrentBucketNum);
- EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
-}
-
-TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
- int tagId = 1;
-
- CountMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
-
- // 2 events in bucket 1.
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
-
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // Flushes at event #2.
- countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
- ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
-
- // Flushes.
- countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
- countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(1UL, buckets.size());
- EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
- EXPECT_EQ(2LL, buckets[0].mCount);
-
- // 1 matched event happens in bucket 2.
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId);
-
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
-
- countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
- countProducer.mPastBuckets.end());
- ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
- EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
- EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
- EXPECT_EQ(1LL, bucketInfo2.mCount);
-
- // nothing happens in bucket 3. we should not record anything for bucket 3.
- countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
- ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
- countProducer.mPastBuckets.end());
- const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(2UL, buckets3.size());
-}
-
-TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-
- CountMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_condition(StringToId("SCREEN_ON"));
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
- protoHash, bucketStartTimeNs, bucketStartTimeNs);
-
- countProducer.onConditionChanged(true, bucketStartTimeNs);
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1);
- countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
-
- ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
-
- countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
-
- // Upon this match event, the matched event1 is flushed.
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1);
- countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
- ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
-
- countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
- countProducer.mPastBuckets.end());
-
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(1UL, buckets.size());
- const auto& bucketInfo = buckets[0];
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
- EXPECT_EQ(1LL, bucketInfo.mCount);
-}
-
-TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-
- int tagId = 1;
- int conditionTagId = 2;
-
- CountMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
- MetricConditionLink* link = metric.add_links();
- link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
- buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
- buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222");
-
- ConditionKey key1;
- key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
- getMockedDimensionKey(conditionTagId, 2, "111")};
-
- ConditionKey key2;
- key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
- getMockedDimensionKey(conditionTagId, 2, "222")};
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
-
- EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
-
- CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
- {ConditionState::kUnknown}, wizard, protoHash,
- bucketStartTimeNs, bucketStartTimeNs);
-
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
- ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
-
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
- countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(1UL, buckets.size());
- const auto& bucketInfo = buckets[0];
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
- EXPECT_EQ(1LL, bucketInfo.mCount);
-}
-
-TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) {
- sp<AlarmMonitor> alarmMonitor;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-
- int tagId = 1;
- int conditionTagId = 2;
-
- CountMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- Alert alert;
- alert.set_num_buckets(3);
- alert.set_trigger_if_sum_gt(2);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
- protoHash, bucketStartTimeNs, bucketStartTimeNs);
-
- sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
- EXPECT_TRUE(anomalyTracker != nullptr);
-
- // Bucket is not flushed yet.
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
- EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-
- // App upgrade or boot complete forces bucket flush.
- // Check that there's a past bucket and the bucket end is not adjusted.
- switch (GetParam()) {
- case APP_UPGRADE:
- countProducer.notifyAppUpgrade(eventTimeNs);
- break;
- case BOOT_COMPLETE:
- countProducer.onStatsdInitCompleted(eventTimeNs);
- break;
- }
- ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs,
- countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ(eventTimeNs,
- countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
- EXPECT_EQ(0, countProducer.getCurrentBucketNum());
- EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
- // Anomaly tracker only contains full buckets.
- EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-
- int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
- // Next event occurs in same bucket as partial bucket created.
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(0, countProducer.getCurrentBucketNum());
- EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-
- // Third event in following bucket.
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1, countProducer.getCurrentBucketNum());
- EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-}
-
-TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
-
- int tagId = 1;
- int conditionTagId = 2;
-
- CountMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
- protoHash, bucketStartTimeNs, bucketStartTimeNs);
-
- // Bucket is flushed yet.
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
-
- // App upgrade or boot complete forces bucket flush.
- // Check that there's a past bucket and the bucket end is not adjusted since the upgrade
- // occurred after the bucket end time.
- switch (GetParam()) {
- case APP_UPGRADE:
- countProducer.notifyAppUpgrade(eventTimeNs);
- break;
- case BOOT_COMPLETE:
- countProducer.onStatsdInitCompleted(eventTimeNs);
- break;
- }
- ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs,
- countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
- EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
-
- // Next event occurs in same bucket as partial bucket created.
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-
- // Third event in following bucket.
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ((int64_t)eventTimeNs,
- countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
-}
-
-TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
- sp<AlarmMonitor> alarmMonitor;
- Alert alert;
- alert.set_id(11);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(2);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 1;
- alert.set_refractory_period_secs(refPeriodSec);
-
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-
- CountMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
-
- sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
-
- int tagId = 1;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId);
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId);
- LogEvent event5(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId);
- LogEvent event6(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId);
- LogEvent event7(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId);
-
- // Two events in bucket #0.
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
- EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-
- // One event in bucket #2. No alarm as bucket #0 is trashed out.
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
- EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-
- // Two events in bucket #3.
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
- ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
- EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
- // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
- ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
- EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- 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,
- protoHash, 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
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
deleted file mode 100644
index d1f89775ed6a..000000000000
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ /dev/null
@@ -1,516 +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.
-
-#include "src/metrics/DurationMetricProducer.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "metrics_test_helper.h"
-#include "src/condition/ConditionWizard.h"
-#include "src/stats_log_util.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-using namespace android::os::statsd;
-using namespace testing;
-using android::sp;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-
-namespace {
-
-const ConfigKey kConfigKey(0, 12345);
-const uint64_t protoHash = 0x1234567890;
-void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-} // namespace
-
-// Setup for parameterized tests.
-class DurationMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
-
-INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket,
- DurationMetricProducerTest_PartialBucket,
- testing::Values(APP_UPGRADE, BOOT_COMPLETE));
-
-TEST(DurationMetricTrackerTest, TestFirstBucket) {
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5,
- 600 * NS_PER_SEC + NS_PER_SEC / 2);
-
- EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
- EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs());
-}
-
-TEST(DurationMetricTrackerTest, TestNoCondition) {
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- int tagId = 1;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId);
-
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
-
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
- durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
- durationProducer.mPastBuckets.end());
- const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(2UL, buckets.size());
- EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
- EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs);
- EXPECT_EQ(2LL, buckets[1].mDuration);
-}
-
-TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- int tagId = 1;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
-
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
- 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
- durationProducer.mCondition = ConditionState::kFalse;
-
- EXPECT_FALSE(durationProducer.mCondition);
- EXPECT_FALSE(durationProducer.isConditionSliced());
-
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
- durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
-
- durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
- durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
- durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
- durationProducer.mPastBuckets.end());
- const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(1UL, buckets2.size());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
- EXPECT_EQ(1LL, buckets2[0].mDuration);
-}
-
-TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- int tagId = 1;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
-
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
- 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
- wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
-
- EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
- EXPECT_FALSE(durationProducer.isConditionSliced());
-
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
- durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
-
- durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
- durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
- durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
- const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(1UL, buckets2.size());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
- EXPECT_EQ(1LL, buckets2[0].mDuration);
-}
-
-TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) {
- /**
- * The duration starts from the first bucket, through the two partial buckets (10-70sec),
- * another bucket, and ends at the beginning of the next full bucket.
- * Expected buckets:
- * - [10,25]: 14 secs
- * - [25,70]: All 45 secs
- * - [70,130]: All 60 secs
- * - [130, 210]: Only 5 secs (event ended at 135sec)
- */
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int tagId = 1;
-
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
-
- int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, startTimeNs, tagId);
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
- EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-
- int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
- switch (GetParam()) {
- case APP_UPGRADE:
- durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- ASSERT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- std::vector<DurationBucket> buckets =
- durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
- EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs);
- EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration);
- EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
-
- // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, endTimeNs, tagId);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
- buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(3UL, buckets.size());
- EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
- EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
-}
-
-TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) {
- /**
- * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
- * - [10,70]: 59 secs
- * - [70,75]: 5 sec
- * - [75,130]: 55 secs
- */
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int tagId = 1;
-
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
-
- int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, startTimeNs, tagId);
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
- EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-
- int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
- switch (GetParam()) {
- case APP_UPGRADE:
- durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- ASSERT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- std::vector<DurationBucket> buckets =
- durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
- EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs);
- EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
- EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
-
- // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, endTimeNs, tagId);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
- buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(3UL, buckets.size());
- EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs,
- buckets[2].mDuration);
-}
-
-TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) {
- sp<AlarmMonitor> alarmMonitor;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int tagId = 1;
-
- // Setup metric with alert.
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
- Alert alert;
- alert.set_num_buckets(3);
- alert.set_trigger_if_sum_gt(2);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
-
- sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
- EXPECT_TRUE(anomalyTracker != nullptr);
-
- int64_t startTimeNs = bucketStartTimeNs + 1;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, startTimeNs, tagId);
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
-
- int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
- switch (GetParam()) {
- case APP_UPGRADE:
- durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
-
- // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, endTimeNs, tagId);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
-
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
- anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-}
-
-TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int tagId = 1;
-
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
-
- int64_t startTimeNs = bucketStartTimeNs + 1;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, startTimeNs, tagId);
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
- EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-
- int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
- switch (GetParam()) {
- case APP_UPGRADE:
- durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
-
- // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, endTimeNs, tagId);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-
- durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
- std::vector<DurationBucket> buckets =
- durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(1UL, buckets.size());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
- EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
-}
-
-TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int tagId = 1;
-
- DurationMetric metric;
- metric.set_id(1);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- FieldMatcher dimensions;
-
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
- bucketStartTimeNs, bucketStartTimeNs);
-
- int64_t startTimeNs = bucketStartTimeNs + 1;
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, startTimeNs, tagId);
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
- EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-
- int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
- switch (GetParam()) {
- case APP_UPGRADE:
- durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
-
- // Stop occurs in the same partial bucket as created for the app upgrade.
- int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, endTimeNs, tagId);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
- ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
-
- durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- std::vector<DurationBucket> buckets =
- durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- ASSERT_EQ(1UL, buckets.size());
- EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
- EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
deleted file mode 100644
index 4bbbd2cb36ad..000000000000
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ /dev/null
@@ -1,186 +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.
-
-#include "src/metrics/EventMetricProducer.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "metrics_test_helper.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-
-namespace {
-const ConfigKey kConfigKey(0, 12345);
-const uint64_t protoHash = 0x1234567890;
-
-void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeString(statsEvent, str.c_str());
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-} // anonymous namespace
-
-TEST(EventMetricProducerTest, TestNoCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-
- EventMetric metric;
- metric.set_id(1);
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, bucketStartTimeNs);
-
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
-
- // Check dump report content.
- ProtoOutputStream output;
- std::set<string> strSet;
- eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
- true /*erase data*/, FAST, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_event_metrics());
- ASSERT_EQ(2, report.event_metrics().data_size());
- EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
- EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos());
-}
-
-TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-
- EventMetric metric;
- metric.set_id(1);
- metric.set_condition(StringToId("SCREEN_ON"));
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, protoHash,
- bucketStartTimeNs);
-
- eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
-
- eventProducer.onConditionChanged(false /*condition*/, bucketStartTimeNs + 2);
-
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
-
- // Check dump report content.
- ProtoOutputStream output;
- std::set<string> strSet;
- eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
- true /*erase data*/, FAST, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_event_metrics());
- ASSERT_EQ(1, report.event_metrics().data_size());
- EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
-}
-
-TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-
- int tagId = 1;
- int conditionTagId = 2;
-
- EventMetric metric;
- metric.set_id(1);
- metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
- MetricConditionLink* link = metric.add_links();
- link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
- buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
- buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111");
- ConditionKey key1;
- key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
- getMockedDimensionKey(conditionTagId, 2, "111")};
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222");
- ConditionKey key2;
- key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
- getMockedDimensionKey(conditionTagId, 2, "222")};
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- // Condition is false for first event.
- EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
- // Condition is true for second event.
- EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
-
- EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, protoHash,
- bucketStartTimeNs);
-
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
-
- // Check dump report content.
- ProtoOutputStream output;
- std::set<string> strSet;
- eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
- true /*erase data*/, FAST, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_event_metrics());
- ASSERT_EQ(1, report.event_metrics().data_size());
- EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
deleted file mode 100644
index 10606810d806..000000000000
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ /dev/null
@@ -1,828 +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.
-
-#include "src/metrics/GaugeMetricProducer.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <math.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "logd/LogEvent.h"
-#include "metrics_test_helper.h"
-#include "src/matchers/SimpleAtomMatchingTracker.h"
-#include "src/metrics/MetricProducer.h"
-#include "src/stats_log_util.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using std::set;
-using std::unordered_map;
-using std::vector;
-using std::make_shared;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-const ConfigKey kConfigKey(0, 12345);
-const int tagId = 1;
-const int64_t metricId = 123;
-const uint64_t protoHash = 0x123456789;
-const int logEventMatcherIndex = 0;
-const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
-const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
-const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-
-shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1,
- int32_t value2) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, value1);
- AStatsEvent_writeString(statsEvent, str1.c_str());
- AStatsEvent_writeInt32(statsEvent, value2);
-
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-} // anonymous namespace
-
-// Setup for parameterized tests.
-class GaugeMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
-
-INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket,
- GaugeMetricProducerTest_PartialBucket,
- testing::Values(APP_UPGRADE, BOOT_COMPLETE));
-
-/*
- * Tests that the first bucket works correctly
- */
-TEST(GaugeMetricProducerTest, TestFirstBucket) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.mutable_gauge_fields_filter()->set_include_all(false);
- auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
- gaugeFieldMatcher->set_field(tagId);
- gaugeFieldMatcher->add_child()->set_field(1);
- gaugeFieldMatcher->add_child()->set_field(3);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- // statsd started long ago.
- // The metric starts in the middle of the bucket
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
-}
-
-TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.mutable_gauge_fields_filter()->set_include_all(false);
- metric.set_max_pull_delay_sec(INT_MAX);
- auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
- gaugeFieldMatcher->set_field(tagId);
- gaugeFieldMatcher->add_child()->set_field(1);
- gaugeFieldMatcher->add_child()->set_field(3);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
- return true;
- }));
-
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11));
-
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
- EXPECT_EQ(INT, it->mValue.getType());
- EXPECT_EQ(10, it->mValue.int_value);
- it++;
- EXPECT_EQ(11, it->mValue.int_value);
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()
- ->second.back()
- .mGaugeAtoms.front()
- .mFields->begin()
- ->mValue.int_value);
-
- allData.clear();
- allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
- EXPECT_EQ(INT, it->mValue.getType());
- EXPECT_EQ(24, it->mValue.int_value);
- it++;
- EXPECT_EQ(INT, it->mValue.getType());
- EXPECT_EQ(25, it->mValue.int_value);
- // One dimension.
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
- it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
- EXPECT_EQ(INT, it->mValue.getType());
- EXPECT_EQ(10L, it->mValue.int_value);
- it++;
- EXPECT_EQ(INT, it->mValue.getType());
- EXPECT_EQ(11L, it->mValue.int_value);
-
- gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
- ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
- // One dimension.
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
- it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
- EXPECT_EQ(INT, it->mValue.getType());
- EXPECT_EQ(24L, it->mValue.int_value);
- it++;
- EXPECT_EQ(INT, it->mValue.getType());
- EXPECT_EQ(25L, it->mValue.int_value);
-}
-
-TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) {
- sp<AlarmMonitor> alarmMonitor;
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.mutable_gauge_fields_filter()->set_include_all(true);
-
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(metricId);
- alert.set_trigger_if_sum_gt(25);
- alert.set_num_buckets(100);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
- EXPECT_TRUE(anomalyTracker != nullptr);
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
-
- switch (GetParam()) {
- case APP_UPGRADE:
- gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs,
- gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ(partialBucketSplitTimeNs,
- gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
- EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
- // Partial buckets are not sent to anomaly tracker.
- EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-
- // Create an event in the same partial bucket.
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs,
- gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ(partialBucketSplitTimeNs,
- gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
- EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
- // Partial buckets are not sent to anomaly tracker.
- EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-
- // Next event should trigger creation of new bucket and send previous full bucket to anomaly
- // tracker.
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum);
- ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-
- // Next event should trigger creation of new bucket.
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
- EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum);
- ASSERT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-}
-
-TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.set_max_pull_delay_sec(INT_MAX);
- auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
- gaugeFieldMatcher->set_field(tagId);
- gaugeFieldMatcher->add_child()->set_field(2);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Return(false))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
- return true;
- }));
-
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
-
- switch (GetParam()) {
- case APP_UPGRADE:
- gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs,
- gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ(partialBucketSplitTimeNs,
- gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
- EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
- ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
-}
-
-TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.set_max_pull_delay_sec(INT_MAX);
- metric.set_split_bucket_for_app_upgrade(false);
- auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
- gaugeFieldMatcher->set_field(tagId);
- gaugeFieldMatcher->add_child()->set_field(2);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Return(false));
-
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
-
- gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- ASSERT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
-}
-
-TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.set_max_pull_delay_sec(INT_MAX);
- auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
- gaugeFieldMatcher->set_field(tagId);
- gaugeFieldMatcher->add_child()->set_field(2);
- metric.set_condition(StringToId("SCREEN_ON"));
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- int64_t conditionChangeNs = bucketStartTimeNs + 8;
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
- return true;
- }));
-
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- gaugeProducer.onConditionChanged(true, conditionChangeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
- ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
- ->second.back()
- .mGaugeAtoms.front()
- .mFields->begin()
- ->mValue.int_value);
-
- gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
- gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
- ->second.back()
- .mGaugeAtoms.front()
- .mFields->begin()
- ->mValue.int_value);
-}
-
-TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
- const int conditionTag = 65;
- GaugeMetric metric;
- metric.set_id(1111111);
- metric.set_bucket(ONE_MINUTE);
- metric.mutable_gauge_fields_filter()->set_include_all(true);
- metric.set_condition(StringToId("APP_DIED"));
- metric.set_max_pull_delay_sec(INT_MAX);
- auto dim = metric.mutable_dimensions_in_what();
- dim->set_field(tagId);
- dim->add_child()->set_field(1);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*wizard, query(_, _, _))
- .WillRepeatedly(
- Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
- const bool isPartialLink) {
- int pos[] = {1, 0, 0};
- Field f(conditionTag, pos, 0);
- HashableDimensionKey key;
- key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
-
- return ConditionState::kTrue;
- }));
-
- int64_t sliceConditionChangeNs = bucketStartTimeNs + 8;
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
- return true;
- }));
-
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
- {ConditionState::kUnknown}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
-
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
- ASSERT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-
- ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-}
-
-TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
- sp<AlarmMonitor> alarmMonitor;
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Return(false));
-
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.set_max_pull_delay_sec(INT_MAX);
- auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
- gaugeFieldMatcher->set_field(tagId);
- gaugeFieldMatcher->add_child()->set_field(2);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(metricId);
- alert.set_trigger_if_sum_gt(25);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 60;
- alert.set_refractory_period_secs(refPeriodSec);
- sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
-
- int tagId = 1;
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-
- std::shared_ptr<LogEvent> event2 =
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15);
-
- allData.clear();
- allData.push_back(event2);
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
-
- allData.clear();
- allData.push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
- ->second.front()
- .mFields->begin()
- ->mValue.int_value);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-
- // This event does not have the gauge field. Thus the current bucket value is 0.
- allData.clear();
- allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10));
- gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
-}
-
-TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
- metric.mutable_gauge_fields_filter()->set_include_all(false);
- metric.set_max_pull_delay_sec(INT_MAX);
- auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
- gaugeFieldMatcher->set_field(tagId);
- gaugeFieldMatcher->add_child()->set_field(1);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
- return true;
- }))
- .WillOnce(Return(true));
-
- int triggerId = 5;
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
-
- LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
- ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
-
- ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size());
- EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin()
- ->second.back()
- .mGaugeAtoms[0]
- .mFields->begin()
- ->mValue.int_value);
- EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin()
- ->second.back()
- .mGaugeAtoms[1]
- .mFields->begin()
- ->mValue.int_value);
-}
-
-TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
- metric.mutable_gauge_fields_filter()->set_include_all(true);
- metric.set_max_pull_delay_sec(INT_MAX);
- auto dimensionMatcher = metric.mutable_dimensions_in_what();
- // use field 1 as dimension.
- dimensionMatcher->set_field(tagId);
- dimensionMatcher->add_child()->set_field(1);
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
- return true;
- }))
- .WillOnce(Return(true));
-
- int triggerId = 5;
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
- ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
- ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
- ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
-
- ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.size());
- auto bucketIt = gaugeProducer.mPastBuckets.begin();
- ASSERT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size());
- EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
- EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
- bucketIt++;
- ASSERT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size());
- EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
- EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
- EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value);
-}
-
-/*
- * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
- * is smaller than the "min_bucket_size_nanos" specified in the metric config.
- */
-TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
- GaugeMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(FIVE_MINUTES);
- metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
- metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
- // Bucket start.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
- return true;
- }));
-
- int triggerId = 5;
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- gaugeProducer.prepareFirstBucket();
-
- LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true,
- FAST /* dump_latency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_gauge_metrics());
- ASSERT_EQ(0, report.gauge_metrics().data_size());
- ASSERT_EQ(1, report.gauge_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.gauge_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
- report.gauge_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
deleted file mode 100644
index fda3daaa56aa..000000000000
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ /dev/null
@@ -1,424 +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.
-
-#include "src/metrics/duration_helper/MaxDurationTracker.h"
-#include "src/condition/ConditionWizard.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-using namespace android::os::statsd;
-using namespace testing;
-using android::sp;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const ConfigKey kConfigKey(0, 12345);
-
-const int TagId = 1;
-
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1");
-const HashableDimensionKey conditionKey = getMockedDimensionKey(TagId, 4, "1");
-const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
-const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
-const int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-
-TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
- const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
- const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
-
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucketNum = 0;
-
- int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
-
- tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
- // Event starts again. This would not change anything as it already starts.
- tracker.noteStart(key1, true, bucketStartTimeNs + 3, ConditionKey());
- // Stopped.
- tracker.noteStop(key1, bucketStartTimeNs + 10, false);
-
- // Another event starts in this bucket.
- tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey());
- tracker.noteStop(key2, bucketStartTimeNs + 40, false /*stop all*/);
-
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(20LL, buckets[eventKey][0].mDuration);
-}
-
-TEST(MaxDurationTrackerTest, TestStopAll) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
- const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
- const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucketNum = 0;
-
- int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
-
- tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
-
- // Another event starts in this bucket.
- tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey());
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets);
- tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
- EXPECT_TRUE(tracker.mInfos.empty());
- EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
-
- tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(bucketSizeNs + 40 - 1, buckets[eventKey][0].mDuration);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[eventKey][0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs);
-}
-
-TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
- const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
- const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucketNum = 0;
-
- int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
-
- // The event starts.
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
-
- // Starts again. Does not DEFAULT_DIMENSION_KEY anything.
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
- ConditionKey());
-
- // The event stops at early 4th bucket.
- // Notestop is called from DurationMetricProducer's onMatchedLogEvent, which calls
- // flushIfneeded.
- tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets);
- tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (3 * bucketSizeNs) + 20,
- false /*stop all*/);
- EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
-
- tracker.flushIfNeeded(bucketStartTimeNs + 4 * bucketSizeNs, &buckets);
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ((3 * bucketSizeNs) + 20 - 1, buckets[eventKey][0].mDuration);
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[eventKey][0].mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs);
-}
-
-TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
- const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
- const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucketNum = 0;
-
- int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
-
- // 2 starts
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, ConditionKey());
- // one stop
- tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/);
-
- tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets);
- // Because of nesting, still not stopped.
- EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
-
- // real stop now.
- tracker.noteStop(DEFAULT_DIMENSION_KEY,
- bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
- tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
-
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(2 * bucketSizeNs + 5 - 1, buckets[eventKey][0].mDuration);
-}
-
-TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
- const HashableDimensionKey conditionDimKey = key1;
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- ConditionKey conditionKey1;
- MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 1, "1");
- conditionKey1[StringToId("APP_BACKGROUND")] = conditionDimKey;
-
- /**
- Start in first bucket, stop in second bucket. Condition turns on and off in the first bucket
- and again turns on and off in the second bucket.
- */
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
- int64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC;
- int64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC;
- int64_t conditionStarts2 = bucketStartTimeNs + bucketSizeNs + 5 * NS_PER_SEC;
- int64_t conditionStops2 = conditionStarts2 + 10 * NS_PER_SEC;
- int64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC;
-
- int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
- 0, bucketStartTimeNs, bucketSizeNs, true, false, {});
- EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
-
- tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
- tracker.noteConditionChanged(key1, true, conditionStarts1);
- tracker.noteConditionChanged(key1, false, conditionStops1);
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- ASSERT_EQ(0U, buckets.size());
-
- tracker.noteConditionChanged(key1, true, conditionStarts2);
- tracker.noteConditionChanged(key1, false, conditionStops2);
- tracker.noteStop(key1, eventStopTimeNs, false);
- tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1, &buckets);
- ASSERT_EQ(1U, buckets.size());
- vector<DurationBucket> item = buckets.begin()->second;
- ASSERT_EQ(1UL, item.size());
- EXPECT_EQ((int64_t)(13LL * NS_PER_SEC), item[0].mDuration);
-}
-
-TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- ConditionKey conditionKey1;
- MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
- conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = 13000000000;
- int64_t durationTimeNs = 2 * 1000;
-
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 45;
- alert.set_refractory_period_secs(refPeriodSec);
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
- {anomalyTracker});
-
- tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
- sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ((long long)(53ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
-
- // Remove the anomaly alarm when the duration is no longer fully met.
- tracker.noteConditionChanged(key1, false, eventStartTimeNs + 15 * NS_PER_SEC);
- ASSERT_EQ(0U, anomalyTracker->mAlarms.size());
-
- // Since the condition was off for 10 seconds, the anomaly should trigger 10 sec later.
- tracker.noteConditionChanged(key1, true, eventStartTimeNs + 25 * NS_PER_SEC);
- ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
- alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ((long long)(63ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
-}
-
-// This tests that we correctly compute the predicted time of an anomaly assuming that the current
-// state continues forward as-is.
-TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- ConditionKey conditionKey1;
- MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
- conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
- ConditionKey conditionKey2;
- conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2");
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- /**
- * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these
- * nested dimensions, we enter the pause state after 3 seconds. When we resume, the second
- * dimension has already been running for 4 seconds. Thus, we have 40-4=36 seconds remaining
- * before we trigger the anomaly.
- */
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start.
- int64_t conditionStarts1 = bucketStartTimeNs + 11 * NS_PER_SEC;
- int64_t conditionStops1 = bucketStartTimeNs + 14 * NS_PER_SEC;
- int64_t conditionStarts2 = bucketStartTimeNs + 20 * NS_PER_SEC;
- int64_t eventStartTimeNs2 = conditionStarts2 - 4 * NS_PER_SEC;
-
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 45;
- alert.set_refractory_period_secs(refPeriodSec);
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
- {anomalyTracker});
-
- tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
- tracker.noteConditionChanged(key1, true, conditionStarts1);
- tracker.noteConditionChanged(key1, false, conditionStops1);
- tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); // Condition is on already.
- tracker.noteConditionChanged(key1, true, conditionStarts2);
- ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
- auto alarm = anomalyTracker->mAlarms.begin()->second;
- int64_t anomalyFireTimeSec = alarm->timestampSec;
- EXPECT_EQ(conditionStarts2 + 36 * NS_PER_SEC,
- (long long)anomalyFireTimeSec * NS_PER_SEC);
-
- // Now we test the calculation now that there's a refractory period.
- // At the correct time, declare the anomaly. This will set a refractory period. Make sure it
- // gets correctly taken into account in future predictAnomalyTimestampNs calculations.
- std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm});
- anomalyTracker->informAlarmsFired(anomalyFireTimeSec * NS_PER_SEC, firedAlarms);
- ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
- int64_t refractoryPeriodEndsSec = anomalyFireTimeSec + refPeriodSec;
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), refractoryPeriodEndsSec);
-
- // Now stop and start again. Make sure the new predictAnomalyTimestampNs takes into account
- // the refractory period correctly.
- int64_t eventStopTimeNs = anomalyFireTimeSec * NS_PER_SEC + 10;
- tracker.noteStop(key1, eventStopTimeNs, false);
- tracker.noteStop(key2, eventStopTimeNs, false);
- tracker.noteStart(key1, true, eventStopTimeNs + 1000000, conditionKey1);
- // Anomaly is ongoing, but we're still in the refractory period.
- ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
- alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ(refractoryPeriodEndsSec, (long long)(alarm->timestampSec));
-
- // Makes sure it is correct after the refractory period is over.
- tracker.noteStop(key1, eventStopTimeNs + 2000000, false);
- int64_t justBeforeRefPeriodNs = (refractoryPeriodEndsSec - 2) * NS_PER_SEC;
- tracker.noteStart(key1, true, justBeforeRefPeriodNs, conditionKey1);
- alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ(justBeforeRefPeriodNs + 40 * NS_PER_SEC,
- (unsigned long long)(alarm->timestampSec * NS_PER_SEC));
-}
-
-// Suppose that within one tracker there are two dimensions A and B.
-// Suppose A starts, then B starts, and then A stops. We still need to set an anomaly based on the
-// elapsed duration of B.
-TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- ConditionKey conditionKey1;
- MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
- conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
- ConditionKey conditionKey2;
- conditionKey2[StringToId("APP_BACKGROUND")] = getMockedDimensionKey(TagId, 4, "2");
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- /**
- * Suppose we have two sub-dimensions that we're taking the MAX over. In the first of these
- * nested dimensions, are started for 8 seconds. When we stop, the other nested dimension has
- * been started for 5 seconds. So we can only allow 35 more seconds from now.
- */
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketEndTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs1 = bucketStartTimeNs + 5 * NS_PER_SEC; // Condition is off at start.
- int64_t eventStopTimeNs1 = bucketStartTimeNs + 13 * NS_PER_SEC;
- int64_t eventStartTimeNs2 = bucketStartTimeNs + 8 * NS_PER_SEC;
-
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 45;
- alert.set_refractory_period_secs(refPeriodSec);
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
- {anomalyTracker});
-
- tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1);
- tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2);
- tracker.noteStop(key1, eventStopTimeNs1, false);
- ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
- auto alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ(eventStopTimeNs1 + 35 * NS_PER_SEC,
- (unsigned long long)(alarm->timestampSec * NS_PER_SEC));
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
deleted file mode 100644
index 1d6f7de5e7e3..000000000000
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ /dev/null
@@ -1,576 +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.
-
-#include "src/metrics/duration_helper/OringDurationTracker.h"
-#include "src/condition/ConditionWizard.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <math.h>
-#include <stdio.h>
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-using namespace testing;
-using android::sp;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-namespace android {
-namespace os {
-namespace statsd {
-
-const ConfigKey kConfigKey(0, 12345);
-const int TagId = 1;
-const int64_t metricId = 123;
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
-
-const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(TagId, 1, "maps");
-const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
-const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-const int64_t bucketSizeNs = 30 * NS_PER_SEC;
-
-TEST(OringDurationTrackerTest, TestDurationOverlap) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- int64_t durationTimeNs = 2 * 1000;
-
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- false, false, {});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
- EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
- EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
-
- tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
- tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
-
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration);
-}
-
-TEST(OringDurationTrackerTest, TestDurationNested) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
-
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
-
- tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false);
- tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
-
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration);
-}
-
-TEST(OringDurationTrackerTest, TestStopAll) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const std::vector<HashableDimensionKey> kConditionKey1 =
- {getMockedDimensionKey(TagId, 1, "maps")};
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
-
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
- tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
-
- tracker.noteStopAll(eventStartTimeNs + 2003);
-
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration);
-}
-
-TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- int64_t durationTimeNs = 2 * 1000;
-
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
- EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
- tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets);
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey());
- EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
-
- ASSERT_EQ(2u, buckets[eventKey].size());
- EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
- EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
-
- tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 10, false);
- tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 12, false);
- tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(2u, buckets[eventKey].size());
- EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
- EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
-}
-
-TEST(OringDurationTrackerTest, TestDurationConditionChange) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- ConditionKey key1;
- key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
-
- EXPECT_CALL(*wizard, query(_, key1, _)) // #4
- .WillOnce(Return(ConditionState::kFalse));
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- int64_t durationTimeNs = 2 * 1000;
-
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- true, false, {});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
-
- tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5);
-
- tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
-
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(5LL, buckets[eventKey][0].mDuration);
-}
-
-TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- ConditionKey key1;
- key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
-
- EXPECT_CALL(*wizard, query(_, key1, _))
- .Times(2)
- .WillOnce(Return(ConditionState::kFalse))
- .WillOnce(Return(ConditionState::kTrue));
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- int64_t durationTimeNs = 2 * 1000;
-
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- true, false, {});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
- // condition to false; record duration 5n
- tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5);
- // condition to true.
- tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 1000);
- // 2nd duration: 1000ns
- tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
-
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(1005LL, buckets[eventKey][0].mDuration);
-}
-
-TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- ConditionKey key1;
- key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
-
- EXPECT_CALL(*wizard, query(_, key1, _)) // #4
- .WillOnce(Return(ConditionState::kFalse));
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
-
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + 1;
-
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
-
- tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false);
-
- tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 15);
-
- tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
-
- tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(15LL, buckets[eventKey][0].mDuration);
-}
-
-TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
- alert.set_num_buckets(2);
- alert.set_refractory_period_secs(1);
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
-
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
- {anomalyTracker});
-
- // Nothing in the past bucket.
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
- EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
- tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
-
- tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 3, false);
- ASSERT_EQ(0u, buckets[eventKey].size());
-
- int64_t event1StartTimeNs = eventStartTimeNs + 10;
- tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey());
- // No past buckets. The anomaly will happen in bucket #0.
- EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
- tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
-
- int64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
- tracker.flushIfNeeded(event1StopTimeNs, &buckets);
- tracker.noteStop(kEventKey1, event1StopTimeNs, false);
-
- EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- ASSERT_EQ(1u, buckets[eventKey].size());
- EXPECT_EQ(3LL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
- buckets[eventKey][0].mDuration);
-
- const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
- const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
-
- // One past buckets. The anomaly will happen in bucket #1.
- int64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
- tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey());
- EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
- bucket1Duration),
- tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
- tracker.noteStop(kEventKey1, event2StartTimeNs + 1, false);
-
- // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
- // bucket #2.
- int64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
- tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey());
- EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
- tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
-}
-
-TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) {
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(5 * NS_PER_SEC);
- alert.set_num_buckets(1);
- alert.set_refractory_period_secs(20);
-
- int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- int64_t bucketNum = 0;
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1,
-
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, true, false, {anomalyTracker});
-
- int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
- // Anomaly happens in the bucket #1.
- EXPECT_EQ((long long)(bucketStartTimeNs + 14 * NS_PER_SEC),
- tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
-
- tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 14 * NS_PER_SEC, false);
-
- EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
-
- int64_t event2StartTimeNs = bucketStartTimeNs + 22 * NS_PER_SEC;
- EXPECT_EQ((long long)(bucketStartTimeNs + 34 * NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY));
- EXPECT_EQ((long long)(bucketStartTimeNs + 35 * NS_PER_SEC),
- tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
-}
-
-TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) {
- // Test the cases where the refractory period is smaller than the bucket size, longer than
- // the bucket size, and longer than 2x of the anomaly detection window.
- for (int j = 0; j < 3; j++) {
- int64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC;
- for (int i = 0; i <= 7; ++i) {
-
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(thresholdNs);
- alert.set_num_buckets(3);
- alert.set_refractory_period_secs(
- bucketSizeNs / NS_PER_SEC / 2 + i * bucketSizeNs / NS_PER_SEC);
-
- int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- int64_t bucketNum = 101;
-
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard,
- 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, true, false, {anomalyTracker});
-
- int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
- EXPECT_EQ((long long)(eventStartTimeNs + thresholdNs),
- tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
- int64_t eventStopTimeNs = eventStartTimeNs + thresholdNs + NS_PER_SEC;
- tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStopTimeNs, false);
-
- int64_t refractoryPeriodEndSec =
- anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY);
- EXPECT_EQ(eventStopTimeNs / (int64_t)NS_PER_SEC + alert.refractory_period_secs(),
- refractoryPeriodEndSec);
-
- // Acquire and release a wakelock in the next bucket.
- int64_t event2StartTimeNs = eventStopTimeNs + bucketSizeNs;
- tracker.noteStart(DEFAULT_DIMENSION_KEY, true, event2StartTimeNs, ConditionKey());
- int64_t event2StopTimeNs = event2StartTimeNs + 4 * NS_PER_SEC;
- tracker.noteStop(DEFAULT_DIMENSION_KEY, event2StopTimeNs, false);
-
- // Test the alarm prediction works well when seeing another wakelock start event.
- for (int k = 0; k <= 2; ++k) {
- int64_t event3StartTimeNs = event2StopTimeNs + NS_PER_SEC + k * bucketSizeNs;
- int64_t alarmTimestampNs =
- tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs);
- EXPECT_GT(alarmTimestampNs, 0u);
- EXPECT_GE(alarmTimestampNs, event3StartTimeNs);
- EXPECT_GE(alarmTimestampNs, refractoryPeriodEndSec *(int64_t) NS_PER_SEC);
- }
- }
- }
-}
-
-TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 45;
- alert.set_refractory_period_secs(refPeriodSec);
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-
- int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- int64_t bucketNum = 0;
- int64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
-
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- false, false, {anomalyTracker});
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
- tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
- EXPECT_TRUE(tracker.mStarted.empty());
- EXPECT_EQ(10LL, tracker.mStateKeyDurationMap[DEFAULT_DIMENSION_KEY].mDuration); // 10ns
-
- ASSERT_EQ(0u, tracker.mStarted.size());
-
- tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
- ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
- EXPECT_EQ((long long)(52ULL * NS_PER_SEC), // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up
- (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
- // The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However,
- // because this is a unit test, the alarm won't actually fire at all. Since the alarm fails
- // to fire in time, the anomaly is instead caught when noteStop is called, at around 71s.
- tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
- tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 25, false);
- EXPECT_EQ(anomalyTracker->getSumOverPastBuckets(eventKey), (long long)(bucketSizeNs));
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey),
- std::ceil((eventStartTimeNs + 2 * bucketSizeNs + 25.0) / NS_PER_SEC + refPeriodSec));
-}
-
-TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
- const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
-
- const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
- const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(40 * NS_PER_SEC);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 45;
- alert.set_refractory_period_secs(refPeriodSec);
-
- unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- ConditionKey conkey;
- conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
- int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
- int64_t bucketSizeNs = 30 * NS_PER_SEC;
-
- sp<AlarmMonitor> alarmMonitor;
- sp<DurationAnomalyTracker> anomalyTracker =
- new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false,
- false, {anomalyTracker});
-
- tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
- ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
- sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
-
- tracker.noteStop(kEventKey1, 17 * NS_PER_SEC, false); // stop key1 (2 seconds later)
- ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
-
- tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
- ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
- alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
-
- tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
- ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
- alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
-
- tracker.noteStop(kEventKey1, 47 * NS_PER_SEC, false); // stop key1
- ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
- alarm = anomalyTracker->mAlarms.begin()->second;
- EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
-
- // Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time.
- std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm});
- anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms);
- ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
-
- tracker.noteStop(kEventKey2, 69 * NS_PER_SEC, false); // stop key2
- ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
deleted file mode 100644
index 6cf4192b41fd..000000000000
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ /dev/null
@@ -1,6919 +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.
-
-#include "src/metrics/ValueMetricProducer.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <math.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "metrics_test_helper.h"
-#include "src/matchers/SimpleAtomMatchingTracker.h"
-#include "src/metrics/MetricProducer.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using std::make_shared;
-using std::set;
-using std::shared_ptr;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-const ConfigKey kConfigKey(0, 12345);
-const int tagId = 1;
-const int64_t metricId = 123;
-const uint64_t protoHash = 0x1234567890;
-const int logEventMatcherIndex = 0;
-const int64_t bucketStartTimeNs = 10000000000;
-const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
-const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
-const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs;
-const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
-double epsilon = 0.001;
-
-static void assertPastBucketValuesSingleKey(
- const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets,
- const std::initializer_list<int>& expectedValuesList,
- const std::initializer_list<int64_t>& expectedDurationNsList,
- const std::initializer_list<int64_t>& expectedStartTimeNsList,
- const std::initializer_list<int64_t>& expectedEndTimeNsList) {
- vector<int> expectedValues(expectedValuesList);
- vector<int64_t> expectedDurationNs(expectedDurationNsList);
- vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList);
- vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList);
-
- ASSERT_EQ(expectedValues.size(), expectedDurationNs.size());
- ASSERT_EQ(expectedValues.size(), expectedStartTimeNs.size());
- ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size());
-
- if (expectedValues.size() == 0) {
- ASSERT_EQ(0, mPastBuckets.size());
- return;
- }
-
- ASSERT_EQ(1, mPastBuckets.size());
- ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
-
- const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second;
- for (int i = 0; i < expectedValues.size(); i++) {
- EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
- << "Values differ at index " << i;
- EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
- << "Condition duration value differ at index " << i;
- EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs)
- << "Start time differs at index " << i;
- EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs)
- << "End time differs at index " << i;
- }
-}
-
-static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition,
- int64_t timerNs, int64_t lastConditionTrueTimestampNs) {
- EXPECT_EQ(condition, conditionTimer.mCondition);
- EXPECT_EQ(timerNs, conditionTimer.mTimerNs);
- EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs);
-}
-
-} // anonymous namespace
-
-class ValueMetricProducerTestHelper {
-public:
- static sp<ValueMetricProducer> createValueProducerNoConditions(
- sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
- .WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
- .WillRepeatedly(Return());
-
- sp<ValueMetricProducer> valueProducer =
- new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer->prepareFirstBucket();
- return valueProducer;
- }
-
- static sp<ValueMetricProducer> createValueProducerWithCondition(
- sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
- ConditionState conditionAfterFirstBucketPrepared) {
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
- .WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
- .WillRepeatedly(Return());
-
- sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
- kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
- protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
- valueProducer->prepareFirstBucket();
- valueProducer->mCondition = conditionAfterFirstBucketPrepared;
- return valueProducer;
- }
-
- static sp<ValueMetricProducer> createValueProducerWithState(
- sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
- vector<int32_t> slicedStateAtoms,
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
- .WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
- .WillRepeatedly(Return());
-
- sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
- kConfigKey, metric, -1 /* no condition */, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
- valueProducer->prepareFirstBucket();
- return valueProducer;
- }
-
- static sp<ValueMetricProducer> createValueProducerWithConditionAndState(
- sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
- vector<int32_t> slicedStateAtoms,
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap,
- ConditionState conditionAfterFirstBucketPrepared) {
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
- .WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
- .WillRepeatedly(Return());
-
- sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
- kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms,
- stateGroupMap);
- valueProducer->prepareFirstBucket();
- valueProducer->mCondition = conditionAfterFirstBucketPrepared;
- return valueProducer;
- }
-
- static ValueMetric createMetric() {
- ValueMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.mutable_value_field()->set_field(tagId);
- metric.mutable_value_field()->add_child()->set_field(2);
- metric.set_max_pull_delay_sec(INT_MAX);
- return metric;
- }
-
- static ValueMetric createMetricWithCondition() {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_condition(StringToId("SCREEN_ON"));
- return metric;
- }
-
- static ValueMetric createMetricWithState(string state) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.add_slice_by_state(StringToId(state));
- return metric;
- }
-
- static ValueMetric createMetricWithConditionAndState(string state) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_condition(StringToId("SCREEN_ON"));
- metric.add_slice_by_state(StringToId(state));
- return metric;
- }
-};
-
-// Setup for parameterized tests.
-class ValueMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
-
-INSTANTIATE_TEST_SUITE_P(ValueMetricProducerTest_PartialBucket,
- ValueMetricProducerTest_PartialBucket,
- testing::Values(APP_UPGRADE, BOOT_COMPLETE));
-
-/*
- * Tests that the first bucket works correctly
- */
-TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- int64_t startTimeBase = 11;
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- // statsd started long ago.
- // The metric starts in the middle of the bucket
- ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- -1, startTimeBase, 22, pullerManager);
- valueProducer.prepareFirstBucket();
-
- EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
- EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
- EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
- valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
- EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
- valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
-}
-
-/*
- * Tests that the first bucket works correctly
- */
-TEST(ValueMetricProducerTest, TestFirstBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- // statsd started long ago.
- // The metric starts in the middle of the bucket
- ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
- valueProducer.prepareFirstBucket();
-
- EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
- EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
-}
-
-/*
- * Tests pulled atoms with no conditions
- */
-TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
-
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(11, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(8, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(23, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(12, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
- EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
- EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(36, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(13, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- ASSERT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
- EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
- EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
- EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
-}
-
-TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Initialize bucket.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
- return true;
- }))
- // Partial bucket.
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- // First bucket ends.
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2));
- valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
-
- // Partial buckets created in 2nd bucket.
- switch (GetParam()) {
- case APP_UPGRADE:
- valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
- EXPECT_EQ(1, valueProducer->getCurrentBucketNum());
-
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3},
- {bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs},
- {bucketStartTimeNs, bucket2StartTimeNs},
- {bucket2StartTimeNs, partialBucketSplitTimeNs});
-}
-
-/*
- * Tests pulled atoms with filtering
- */
-TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- FieldValueMatcher fvm;
- fvm.set_field(1);
- fvm.set_eq_int(3);
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex, {fvm});
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard,
- protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer->prepareFirstBucket();
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11));
-
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(11, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(8, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // No new data seen, so data has been cleared.
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(11, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(8, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- // the base was reset
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(36, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
- EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-}
-
-/*
- * Tests pulled atoms with no conditions and take absolute value after reset
- */
-TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_use_absolute_value_on_reset(true);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Return(true));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
-
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(11, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(10, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(10, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(36, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(26, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
- EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
- EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
-}
-
-/*
- * Tests pulled atoms with no conditions and take zero value after reset
- */
-TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Return(false));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
-
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(11, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(10, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(36, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(26, curInterval.value.long_value);
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-}
-
-/*
- * Test pulled event with non sliced condition.
- */
-TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- // startUpdated:false sum:0 start:100
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(100, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(110, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(10, curInterval.value.long_value);
-
- valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(20, curInterval.value.long_value);
- EXPECT_EQ(false, curBaseInfo.hasBase);
-
- valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1},
- {bucketStartTimeNs, bucket2StartTimeNs},
- {bucket2StartTimeNs, bucket3StartTimeNs});
-}
-
-TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-
- int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
- switch (GetParam()) {
- case APP_UPGRADE:
- valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10},
- {partialBucketSplitTimeNs - bucketStartTimeNs},
- {bucketStartTimeNs}, {partialBucketSplitTimeNs});
- EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(0, valueProducer.getCurrentBucketNum());
-
- // Event arrives after the bucket split.
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 20);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10},
- {partialBucketSplitTimeNs - bucketStartTimeNs},
- {bucketStartTimeNs}, {partialBucketSplitTimeNs});
- EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(0, valueProducer.getCurrentBucketNum());
-
- // Next value should create a new bucket.
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10, 20},
- {partialBucketSplitTimeNs - bucketStartTimeNs,
- bucket2StartTimeNs - partialBucketSplitTimeNs},
- {bucketStartTimeNs, partialBucketSplitTimeNs},
- {partialBucketSplitTimeNs, bucket2StartTimeNs});
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1, valueProducer.getCurrentBucketNum());
-}
-
-TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Return(true))
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
- return true;
- }));
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
-
- valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-
- switch (GetParam()) {
- case APP_UPGRADE:
- valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1, valueProducer.getCurrentBucketNum());
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}, {bucket2StartTimeNs},
- {partialBucketSplitTimeNs});
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150));
- valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(2, valueProducer.getCurrentBucketNum());
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {150, bucketSizeNs - 150},
- {bucket2StartTimeNs, partialBucketSplitTimeNs},
- {partialBucketSplitTimeNs, bucket3StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_split_bucket_for_app_upgrade(false);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Return(true));
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
-
- valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-
- valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150);
- ASSERT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
-}
-
-TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs,
- bucket2StartTimeNs - 100); // Condition change to false time.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 120));
- return true;
- }));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
-
- valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
- EXPECT_FALSE(valueProducer->mCondition);
-
- int64_t partialBucketSplitTimeNs = bucket2StartTimeNs - 50;
- switch (GetParam()) {
- case APP_UPGRADE:
- valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- // Expect one full buckets already done and starting a partial bucket.
- EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
- EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
- {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)},
- {bucketStartTimeNs}, {partialBucketSplitTimeNs});
- EXPECT_FALSE(valueProducer->mCondition);
-}
-
-TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(30, curInterval.value.long_value);
-
- valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
- protoHash, logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
- valueProducer.mCondition = ConditionState::kFalse;
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- // has 1 slice
- ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
-
- valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(20, curInterval.value.long_value);
-
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(50, curInterval.value.long_value);
-
- valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
-
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(50, curInterval.value.long_value);
-
- valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}, {bucketStartTimeNs},
- {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestAnomalyDetection) {
- sp<AlarmMonitor> alarmMonitor;
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(metricId);
- alert.set_trigger_if_sum_gt(130);
- alert.set_num_buckets(2);
- const int32_t refPeriodSec = 3;
- alert.set_refractory_period_secs(refPeriodSec);
-
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
- wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
- -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
- valueProducer.prepareFirstBucket();
-
- sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 1 * NS_PER_SEC, 10);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 2 + NS_PER_SEC, 20);
-
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event3, tagId,
- bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, 130);
-
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event4, tagId,
- bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC, 1);
-
- LogEvent event5(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event5, tagId,
- bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, 150);
-
- LogEvent event6(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event6, tagId,
- bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC, 160);
-
- // Two events in bucket #0.
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- // Value sum == 30 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-
- // One event in bucket #2. No alarm as bucket #0 is trashed out.
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- // Value sum == 130 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-
- // Three events in bucket #3.
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
- // Anomaly at event 4 since Value sum == 131 > 130!
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
- // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
- // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-}
-
-TEST(ValueMetricProducerTest, TestAnomalyDetectionMultipleBucketsSkipped) {
- sp<AlarmMonitor> alarmMonitor;
- Alert alert;
- alert.set_id(101);
- alert.set_metric_id(metricId);
- alert.set_trigger_if_sum_gt(100);
- alert.set_num_buckets(1);
- const int32_t refPeriodSec = 3;
- alert.set_refractory_period_secs(refPeriodSec);
-
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 0));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs,
- bucket3StartTimeNs + 100); // Condition changed to false time.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 100, 120));
- return true;
- }));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
- sp<AnomalyTracker> anomalyTracker = valueProducer->addAnomalyTracker(alert, alarmMonitor);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
-
- // multiple buckets should be skipped here.
- valueProducer->onConditionChanged(false, bucket3StartTimeNs + 100);
-
- // No alert is fired when multiple buckets are skipped.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-}
-
-// Test value metric no condition, the pull on bucket boundary come in time and too late
-TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Return(true));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- vector<shared_ptr<LogEvent>> allData;
- // pull 1
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
-
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- // startUpdated:true sum:0 start:11
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(11, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- // pull 2 at correct time
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- // tartUpdated:false sum:12
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(23, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
- {bucket2StartTimeNs}, {bucket3StartTimeNs});
-
- // pull 3 come late.
- // The previous bucket gets closed with error. (Has start value 23, no ending)
- // Another bucket gets closed with error. (No start, but ending with 36)
- // The new bucket is back to normal.
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- // startUpdated:false sum:12
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(36, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
- {bucket2StartTimeNs}, {bucket3StartTimeNs});
- // The 1st bucket is dropped because of no data
- // The 3rd bucket is dropped due to multiple buckets being skipped.
- ASSERT_EQ(2, valueProducer->mSkippedBuckets.size());
-
- EXPECT_EQ(bucketStartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
- EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
- ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size());
- EXPECT_EQ(NO_DATA, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
- EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs);
-
- EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[1].bucketStartTimeNs);
- EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].bucketEndTimeNs);
- ASSERT_EQ(1, valueProducer->mSkippedBuckets[1].dropEvents.size());
- EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[1].dropEvents[0].reason);
- EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].dropEvents[0].dropTimeNs);
-}
-
-/*
- * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
- * was delivered late.
- */
-TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
- return true;
- }))
- // condition becomes false
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
- return true;
- }));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(100, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- // pull on bucket boundary come late, condition change happens before it
- valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
- EXPECT_EQ(false, curBaseInfo.hasBase);
-
- // Now the alarm is delivered.
- // since the condition turned to off before this pull finish, it has no effect
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
-}
-
-/*
- * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
- * change to false, and then true again. This is due to alarm delivered late.
- */
-TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
- return true;
- }))
- // condition becomes false
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
- return true;
- }))
- // condition becomes true again
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- // startUpdated:false sum:0 start:100
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(100, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- // pull on bucket boundary come late, condition change happens before it
- valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
-
- // condition changed to true again, before the pull alarm is delivered
- valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(130, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
-
- // Now the alarm is delivered, but it is considered late, the data will be used
- // for the new bucket since it was just pulled.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
-
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(140, curBaseInfo.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(10, curInterval.value.long_value);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- assertPastBucketValuesSingleKey(
- valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24},
- {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_aggregation_type(ValueMetric::MIN);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(10, curInterval.value.long_value);
-
- valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_aggregation_type(ValueMetric::MAX);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(20, curInterval.value.long_value);
-
- valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_aggregation_type(ValueMetric::AVG);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval;
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(1, curInterval.sampleSize);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(25, curInterval.value.long_value);
- EXPECT_EQ(2, curInterval.sampleSize);
-
- valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer.mPastBuckets.size());
- ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-
- EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value -
- 12.5) < epsilon);
-}
-
-TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_aggregation_type(ValueMetric::SUM);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(25, curInterval.value.long_value);
-
- valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_aggregation_type(ValueMetric::MIN);
- metric.set_use_diff(true);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(10, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(5, curInterval.value.long_value);
-
- // no change in data.
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
-
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(15, curBaseInfo.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(0, curInterval.value.long_value);
-
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(15, curBaseInfo.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(0, curInterval.value.long_value);
-
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.mutable_value_field()->add_child()->set_field(3);
- metric.set_aggregation_type(ValueMetric::MIN);
- metric.set_use_diff(true);
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, -1,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateThreeValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10, 20);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateThreeValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15, 22);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(10, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(20, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(5, curInterval.value.long_value);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(2, curInterval.value.long_value);
-
- // no change in first value field
- LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateThreeValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15, 25);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
-
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(15, curBaseInfo.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(25, curBaseInfo.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
-
- LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateThreeValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15, 29);
-
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
- ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(15, curBaseInfo.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
- curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(29, curBaseInfo.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
-
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-
- ASSERT_EQ(1UL, valueProducer.mPastBuckets.size());
- ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
- ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size());
- ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size());
-
- EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs);
- EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
- EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]);
- EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value);
- EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]);
-
- EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[1].mConditionTrueNs);
- EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
- EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]);
-}
-
-/*
- * Tests zero default base.
- */
-TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
- metric.set_use_zero_default_base(true);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- auto iter = valueProducer->mCurrentSlicedBucket.begin();
- auto& interval1 = iter->second.intervals[0];
- auto iterBase = valueProducer->mCurrentBaseInfo.begin();
- auto& baseInfo1 = iterBase->second.baseInfos[0];
- EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, baseInfo1.hasBase);
- EXPECT_EQ(3, baseInfo1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
- vector<shared_ptr<LogEvent>> allData;
-
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
-
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, baseInfo1.hasBase);
- EXPECT_EQ(11, baseInfo1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(8, interval1.value.long_value);
-
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
- if (it != iter) {
- break;
- }
- }
- auto itBase = valueProducer->mCurrentBaseInfo.begin();
- for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
- if (itBase != iterBase) {
- break;
- }
- }
- EXPECT_TRUE(it != iter);
- EXPECT_TRUE(itBase != iterBase);
- auto& interval2 = it->second.intervals[0];
- auto& baseInfo2 = itBase->second.baseInfos[0];
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, baseInfo2.hasBase);
- EXPECT_EQ(4, baseInfo2.base.long_value);
- EXPECT_EQ(false, interval2.hasValue);
- EXPECT_EQ(4, interval2.value.long_value);
-
- ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
- auto iterator = valueProducer->mPastBuckets.begin();
- EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
- EXPECT_EQ(8, iterator->second[0].values[0].long_value);
- iterator++;
- EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
- EXPECT_EQ(4, iterator->second[0].values[0].long_value);
-}
-
-/*
- * Tests using zero default base with failed pull.
- */
-TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
- metric.set_use_zero_default_base(true);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- const auto& it = valueProducer->mCurrentSlicedBucket.begin();
- ValueMetricProducer::Interval& interval1 = it->second.intervals[0];
- ValueMetricProducer::BaseInfo& baseInfo1 =
- valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())
- ->second.baseInfos[0];
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, baseInfo1.hasBase);
- EXPECT_EQ(3, baseInfo1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
- vector<shared_ptr<LogEvent>> allData;
-
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
-
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, baseInfo1.hasBase);
- EXPECT_EQ(11, baseInfo1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(8, interval1.value.long_value);
-
- auto it2 = valueProducer->mCurrentSlicedBucket.begin();
- for (; it2 != valueProducer->mCurrentSlicedBucket.end(); it2++) {
- if (it2 != it) {
- break;
- }
- }
- EXPECT_TRUE(it2 != it);
- ValueMetricProducer::Interval& interval2 = it2->second.intervals[0];
- ValueMetricProducer::BaseInfo& baseInfo2 =
- valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())
- ->second.baseInfos[0];
- EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, baseInfo2.hasBase);
- EXPECT_EQ(4, baseInfo2.base.long_value);
- EXPECT_EQ(false, interval2.hasValue);
- EXPECT_EQ(4, interval2.value.long_value);
- ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
-
- // next pull somehow did not happen, skip to end of bucket 3
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, baseInfo2.hasBase);
- EXPECT_EQ(5, baseInfo2.base.long_value);
- EXPECT_EQ(false, interval2.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
-
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 13));
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 5));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
-
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Get new references now that entries have been deleted from the map
- const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
- const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
- ASSERT_EQ(it3->second.intervals.size(), 1);
- ASSERT_EQ(it4->second.intervals.size(), 1);
- ValueMetricProducer::Interval& interval3 = it3->second.intervals[0];
- ValueMetricProducer::Interval& interval4 = it4->second.intervals[0];
- ValueMetricProducer::BaseInfo& baseInfo3 =
- valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())
- ->second.baseInfos[0];
- ValueMetricProducer::BaseInfo& baseInfo4 =
- valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())
- ->second.baseInfos[0];
-
- EXPECT_EQ(true, baseInfo3.hasBase);
- EXPECT_EQ(5, baseInfo3.base.long_value);
- EXPECT_EQ(false, interval3.hasValue);
- EXPECT_EQ(5, interval3.value.long_value);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-
- EXPECT_EQ(true, baseInfo4.hasBase);
- EXPECT_EQ(13, baseInfo4.base.long_value);
- EXPECT_EQ(false, interval4.hasValue);
- EXPECT_EQ(8, interval4.value.long_value);
-
- ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
-}
-
-/*
- * Tests trim unused dimension key if no new data is seen in an entire bucket.
- */
-TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- auto iter = valueProducer->mCurrentSlicedBucket.begin();
- auto& interval1 = iter->second.intervals[0];
- auto iterBase = valueProducer->mCurrentBaseInfo.begin();
- auto& baseInfo1 = iterBase->second.baseInfos[0];
- EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, baseInfo1.hasBase);
- EXPECT_EQ(3, baseInfo1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, baseInfo1.hasBase);
- EXPECT_EQ(11, baseInfo1.base.long_value);
- EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(8, interval1.value.long_value);
- EXPECT_FALSE(interval1.seenNewData);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
- if (it != iter) {
- break;
- }
- }
- auto itBase = valueProducer->mCurrentBaseInfo.begin();
- for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
- if (itBase != iterBase) {
- break;
- }
- }
- EXPECT_TRUE(it != iter);
- EXPECT_TRUE(itBase != iterBase);
- auto interval2 = it->second.intervals[0];
- auto baseInfo2 = itBase->second.baseInfos[0];
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, baseInfo2.hasBase);
- EXPECT_EQ(4, baseInfo2.base.long_value);
- EXPECT_EQ(false, interval2.hasValue);
- EXPECT_FALSE(interval2.seenNewData);
-
- // next pull somehow did not happen, skip to end of bucket 3
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- // Only one interval left. One was trimmed.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, baseInfo2.hasBase);
- EXPECT_EQ(5, baseInfo2.base.long_value);
- EXPECT_EQ(false, interval2.hasValue);
- EXPECT_FALSE(interval2.seenNewData);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
-
- interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, baseInfo2.hasBase);
- EXPECT_EQ(14, baseInfo2.base.long_value);
- EXPECT_EQ(false, interval2.hasValue);
- EXPECT_FALSE(interval2.seenNewData);
- ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
- auto iterator = valueProducer->mPastBuckets.begin();
- EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs);
- EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs);
- EXPECT_EQ(9, iterator->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
- iterator++;
- EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs);
- EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs);
- EXPECT_EQ(8, iterator->second[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
-}
-
-TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- // Used by onConditionChanged.
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo& curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(100, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
-
- vector<shared_ptr<LogEvent>> allData;
- valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(false, valueProducer->mHasGlobalBase);
-}
-
-TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
- return true;
- }))
- .WillOnce(Return(false));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo& curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(100, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
-
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 20);
-
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, valueProducer->mHasGlobalBase);
-}
-
-TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
- return false;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false.
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Don't directly set mCondition; the real code never does that. Go through regular code path
- // to avoid unexpected behaviors.
- // valueProducer->mCondition = ConditionState::kTrue;
- valueProducer->onConditionChanged(true, bucketStartTimeNs);
-
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(false, valueProducer->mHasGlobalBase);
-}
-
-TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_condition(StringToId("SCREEN_ON"));
- metric.set_max_pull_delay_sec(0);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Max delay is set to 0 so pull will exceed max delay.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-}
-
-TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
-
- ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
- protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
- bucket2StartTimeNs, bucket2StartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
- valueProducer.mCondition = ConditionState::kFalse;
-
- // Event should be skipped since it is from previous bucket.
- // Pull should not be called.
- valueProducer.onConditionChanged(true, bucketStartTimeNs);
- ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
-}
-
-TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
- valueProducer->mHasGlobalBase = false;
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
- valueProducer->mHasGlobalBase = true;
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(100, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-}
-
-/*
- * Tests that a bucket is marked invalid when a condition change pull fails.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First onConditionChanged
- .WillOnce(Return(false))
- // Second onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kTrue);
-
- // Bucket start.
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
-
- // This will fail and should invalidate the whole bucket since we do not have all the data
- // needed to compute the metric value when the screen was on.
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
-
- // Bucket end.
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
-
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
- // Contains base from last pull which was successful.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(140, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 10, false /* include partial bucket */, true,
- FAST /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
-}
-
-/*
- * Tests that a bucket is marked invalid when the guardrail is hit.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
- metric.set_condition(StringToId("SCREEN_ON"));
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _))
- // First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- for (int i = 0; i < 2000; i++) {
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
- }
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
- EXPECT_EQ(true, valueProducer->mCurrentBucketIsSkipped);
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size());
-
- // Bucket 2 start.
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 10));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // First bucket added to mSkippedBuckets after flush.
- ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
- true, FAST /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::DIMENSION_GUARDRAIL_REACHED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
-}
-
-/*
- * Tests that a bucket is marked invalid when the bucket's initial pull fails.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
- return true;
- }))
- // Second onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kTrue);
-
- // Bucket start.
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
- valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
-
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
-
- // Bucket end.
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
-
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
- // Contains base from last pull which was successful.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(140, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
- true, FAST /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
-}
-
-/*
- * Tests that a bucket is marked invalid when the bucket's final pull fails
- * (i.e. failed pull on bucket boundary).
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
- return true;
- }))
- // Second onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kTrue);
-
- // Bucket start.
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
-
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
-
- // Bucket end.
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
- valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
-
- valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
-
- ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
- // Last pull failed so base has been reset.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(false, valueProducer->mHasGlobalBase);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
- true, FAST /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
-}
-
-TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- // Start bucket.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- // Bucket 2 start.
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
-
- // Bucket 3 empty.
- allData.clear();
- allData.push_back(CreateNoValuesLogEvent(tagId, bucket3StartTimeNs + 1));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- // Data has been trimmed.
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
-}
-
-TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-
- // Empty pull.
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(false, valueProducer->mHasGlobalBase);
-}
-
-TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval& curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-
- // End of bucket
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- // Data is empty, base should be reset.
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(5, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-
- ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
- metric.set_condition(StringToId("SCREEN_ON"));
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
- // First onConditionChanged
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-
- // End of bucket
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // Key 1 should be reset since in not present in the most pull.
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- auto iterator = valueProducer->mCurrentSlicedBucket.begin();
- auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
- EXPECT_EQ(true, baseInfoIter->second.baseInfos[0].hasBase);
- EXPECT_EQ(2, baseInfoIter->second.baseInfos[0].base.long_value);
- EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
- iterator++;
- baseInfoIter++;
- EXPECT_EQ(false, baseInfoIter->second.baseInfos[0].hasBase);
- EXPECT_EQ(1, baseInfoIter->second.baseInfos[0].base.long_value);
- EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
-
- EXPECT_EQ(true, valueProducer->mHasGlobalBase);
-}
-
-TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Initialization.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
- // notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
- return true;
- }));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
- ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
-
- switch (GetParam()) {
- case APP_UPGRADE:
- valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
- EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
- EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
- {partialBucketSplitTimeNs - bucketStartTimeNs},
- {bucketStartTimeNs}, {partialBucketSplitTimeNs});
- ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size());
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4));
- // Pull fails and arrives late.
- valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
- {partialBucketSplitTimeNs - bucketStartTimeNs},
- {bucketStartTimeNs}, {partialBucketSplitTimeNs});
- ASSERT_EQ(1, valueProducer->mSkippedBuckets.size());
- ASSERT_EQ(2, valueProducer->mSkippedBuckets[0].dropEvents.size());
- EXPECT_EQ(PULL_FAILED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
- EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[1].reason);
- EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
- EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
- ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
-}
-
-TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Second onConditionChanged.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
- return true;
- }))
- // Third onConditionChanged.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kUnknown);
-
- valueProducer->onConditionChanged(false, bucketStartTimeNs);
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-
- // End of first bucket
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 4));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-
- valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curBaseInfo.hasBase);
- EXPECT_EQ(5, curBaseInfo.base.long_value);
- EXPECT_EQ(false, curInterval.hasValue);
-
- valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
-
- // Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10},
- {bucket2StartTimeNs}, {bucket3StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_use_diff(false);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- // Initialization.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST_P(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Initialization.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
- // notifyAppUpgrade.
- .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
- const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- switch (GetParam()) {
- case APP_UPGRADE:
- valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
- break;
- case BOOT_COMPLETE:
- valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
- break;
- }
-
- // Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First on condition changed.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
- // Second on condition changed.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(2, curInterval.value.long_value);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
-
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {bucketStartTimeNs},
- {bucket2StartTimeNs});
-}
-
-// TODO: b/145705635 fix or delete this test
-TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First condition change.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
- return true;
- }))
- // 2nd condition change.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
- return true;
- }))
- // 3rd condition change.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
- valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 3, 10));
- valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
- valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
-
- valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8);
- valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // There was not global base available so all buckets are invalid.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
-}
-
-TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
-
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- // Initial pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
- return true;
- }));
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
- FAST, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- // Bucket is invalid since we did not pull when dump report was called.
- ASSERT_EQ(0, report.value_metrics().data_size());
-}
-
-TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
-
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
- // Initial pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
- return true;
- }));
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, tagId, 2, 2));
- valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- // Previous bucket is part of the report.
- ASSERT_EQ(1, report.value_metrics().data_size());
- EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num());
-}
-
-TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-
- sp<EventMatcherWizard> eventMatcherWizard =
- createEventMatcherWizard(tagId, logEventMatcherIndex);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
-
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Initial pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
- return true;
- }))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(
- CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10, tagId, 3, 3));
- return true;
- }));
-
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
- logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
-
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- ASSERT_EQ(1, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
- EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
-}
-
-TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.set_use_diff(false);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 10));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30);
-
- // Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
-}
-
-TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
- metric.set_use_diff(false);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
- return true;
- }))
- // condition becomes false
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
- return true;
- }));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
- // has one slice
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(true, curInterval.hasValue);
- EXPECT_EQ(20, curInterval.value.long_value);
-
- // Now the alarm is delivered. Condition is off though.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
- curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
-}
-
-TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
- metric.set_use_diff(false);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
- // condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
- return true;
- }));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-
- // Now the alarm is delivered. Condition is off though.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
- {bucketStartTimeNs}, {bucket2StartTimeNs});
- ValueMetricProducer::Interval curInterval =
- valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
- ValueMetricProducer::BaseInfo curBaseInfo =
- valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
- EXPECT_EQ(false, curBaseInfo.hasBase);
- EXPECT_EQ(false, curInterval.hasValue);
-}
-
-TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
- metric.set_use_diff(false);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Now the alarm is delivered. Condition is off though.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // Condition was always false.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
-}
-
-TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
- metric.set_use_diff(false);
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // condition becomes true
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
- return true;
- }))
- .WillOnce(Return(false));
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
-
- // Now the alarm is delivered. Condition is off though.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // No buckets, we had a failure.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
-}
-
-/*
- * Test that DUMP_REPORT_REQUESTED dump reason is logged.
- *
- * For the bucket to be marked invalid during a dump report requested,
- * three things must be true:
- * - we want to include the current partial bucket
- * - we need a pull (metric is pulled and condition is true)
- * - the dump latency must be FAST
- */
-
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true,
- FAST /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition
- * change event (i.e. the condition change occurs in the wrong bucket).
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-
- // Bucket boundary pull.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
- valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
-
- // Late condition change event.
- valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(1, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(1);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate
- * event (i.e. the accumulate events call occurs in the wrong bucket).
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
- return true;
- }))
- // Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-
- // Bucket boundary pull.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
- valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
-
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 20));
-
- // Late accumulateEvents event.
- valueProducer->accumulateEvents(allData, bucket2StartTimeNs - 100, bucket2StartTimeNs - 100);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(1, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
- * when a metric is initialized.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
- return true;
- }))
- // Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kUnknown);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
- valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that PULL_FAILED dump reason is logged due to a pull failure in
- * #pullAndMatchEventsLocked.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
- return true;
- }))
- // Dump report requested, pull fails.
- .WillOnce(Return(false));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
- valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event
- * skips over more than one bucket.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
- return true;
- }))
- // Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
-
- // Condition change event that skips forward by three buckets.
- valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10);
-
- int64_t dumpTimeNs = bucket4StartTimeNs + 1000;
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(dumpTimeNs, true /* include current buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(2, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::MULTIPLE_BUCKETS_SKIPPED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucket4StartTimeNs + 10), dropEvent.drop_time_millis());
-
- // This bucket is skipped because a dumpReport with include current buckets is called.
- // This creates a new bucket from bucket4StartTimeNs to dumpTimeNs in which we have no data
- // since the condition is false for the entire bucket interval.
- EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
- report.value_metrics().skipped(1).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpTimeNs),
- report.value_metrics().skipped(1).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
-
- dropEvent = report.value_metrics().skipped(1).drop_event(0);
- EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(dumpTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
- * is smaller than the "min_bucket_size_nanos" specified in the metric config.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
- metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
- return true;
- }))
- // Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 9000000, 15));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
- valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that NO_DATA dump reason is logged when a flushed bucket contains no data.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kFalse);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
- valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that all buckets are dropped due to condition unknown until the first onConditionChanged.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10));
- return true;
- }))
- // Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kUnknown);
-
- // Bucket should be dropped because of condition unknown.
- int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC;
- valueProducer->notifyAppUpgrade(appUpgradeTimeNs);
-
- // Bucket also dropped due to condition unknown
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // This bucket is also dropped due to condition unknown.
- int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC;
- valueProducer->onConditionChanged(true, conditionChangeTimeNs);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds
- valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(3, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis());
-
- EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
- report.value_metrics().skipped(1).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(1).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
-
- dropEvent = report.value_metrics().skipped(1).drop_event(0);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
-
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(2).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
- report.value_metrics().skipped(2).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size());
-
- dropEvent = report.value_metrics().skipped(2).drop_event(0);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket
- * was not flushed in time.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenForceBucketSplitBeforeBucketFlush) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
- return true;
- }))
- // App Update.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1000, 15));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
- ConditionState::kFalse);
-
- // Condition changed event
- int64_t conditionChangeTimeNs = bucketStartTimeNs + 10;
- valueProducer->onConditionChanged(true, conditionChangeTimeNs);
-
- // App update event.
- int64_t appUpdateTimeNs = bucket2StartTimeNs + 1000;
- valueProducer->notifyAppUpgrade(appUpdateTimeNs);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000; // 10 seconds
- valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(1, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
- auto data = report.value_metrics().data(0);
- ASSERT_EQ(0, data.bucket_info(0).bucket_num());
- EXPECT_EQ(5, data.bucket_info(0).values(0).value_long());
-
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test multiple bucket drop events in the same bucket.
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
- // Condition change to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kUnknown);
-
- // Condition change event.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
- valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
- FAST /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(1);
- EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
-}
-
-/*
- * Test that the number of logged bucket drop events is capped at the maximum.
- * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached().
- */
-TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // First condition change event.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
- for (int i = 0; i < 2000; i++) {
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
- }
- return true;
- }))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Return(false))
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kUnknown);
-
- // First condition change event causes guardrail to be reached.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
-
- // 2-10 condition change events result in failed pulls.
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 30);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 70);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 90);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 100);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 150);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 170);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 190);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 200);
-
- // Condition change event 11
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 220);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
- // Because we already have 10 dump events in the current bucket,
- // this case should not be added to the list of dump events.
- valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true,
- FAST /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(10, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(1);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 30), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(2);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 50), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(3);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 70), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(4);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 90), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(5);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(6);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 150), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(7);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 170), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(8);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 190), dropEvent.drop_time_millis());
-
- dropEvent = report.value_metrics().skipped(0).drop_event(9);
- EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 200), dropEvent.drop_time_millis());
-}
-
-/*
- * Test metric with a simple sliced state
- * - Increasing values
- * - Using diff
- * - Second field is value field
- */
-TEST(ValueMetricProducerTest, TestSlicedState) {
- // Set up ValueMetricProducer.
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // ValueMetricProducer initialized.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- // Screen state change to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
- return true;
- }))
- // Screen state change to OFF.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9));
- return true;
- }))
- // Screen state change to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
- return true;
- }))
- // Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithState(
- pullerManager, metric, {util::SCREEN_STATE_CHANGED}, {});
- EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- // Bucket status after metric initialized.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
-
- // Bucket status after screen state change kStateUnknown->ON.
- auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_EQ(0, it->second.intervals.size());
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
- // Value for dimension, state key {{}, kStateUnknown}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 5 * NS_PER_SEC);
-
- // Bucket status after screen state change ON->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, OFF}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_EQ(0, it->second.intervals.size());
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
- // Value for dimension, state key {{}, ON}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(4, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 10 * NS_PER_SEC);
- // Value for dimension, state key {{}, kStateUnknown}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 5 * NS_PER_SEC);
-
- // Bucket status after screen state change OFF->ON.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, OFF}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(12, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 15 * NS_PER_SEC);
- // Value for dimension, state key {{}, ON}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(4, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC,
- bucketStartTimeNs + 15 * NS_PER_SEC);
- // Value for dimension, state key {{}, kStateUnknown}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 5 * NS_PER_SEC);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(3, report.value_metrics().data_size());
-
- // {{}, kStateUnknown}
- auto data = report.value_metrics().data(0);
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
- EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{}, ON}
- data = report.value_metrics().data(1);
- ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
- EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
- EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{}, OFF}
- data = report.value_metrics().data(2);
- ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
- EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
- EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-}
-
-/*
- * Test metric with sliced state with map
- * - Increasing values
- * - Using diff
- * - Second field is value field
- */
-TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
- // Set up ValueMetricProducer.
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // ValueMetricProducer initialized.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- // Screen state change to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
- return true;
- }))
- // Screen state change to VR has no pull because it is in the same
- // state group as ON.
-
- // Screen state change to ON has no pull because it is in the same
- // state group as VR.
-
- // Screen state change to OFF.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
- return true;
- }))
- // Dump report requested.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
- return true;
- }));
-
- const StateMap& stateMap =
- CreateScreenStateOnOffMap(/*screen on id=*/321, /*screen off id=*/123);
- const StateMap_StateGroup screenOnGroup = stateMap.group(0);
- const StateMap_StateGroup screenOffGroup = stateMap.group(1);
-
- unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
- for (auto group : stateMap.group()) {
- for (auto value : group.value()) {
- stateGroupMap[SCREEN_STATE_ATOM_ID][value] = group.group_id();
- }
- }
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithState(
- pullerManager, metric, {util::SCREEN_STATE_CHANGED}, stateGroupMap);
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
-
- // Bucket status after metric initialized.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, {kStateUnknown}}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
-
- // Bucket status after screen state change kStateUnknown->ON.
- auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- itBase->second.currentState.getValues()[0].mValue.long_value);
- // Value for dimension, state key {{}, ON GROUP}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
- // Value for dimension, state key {{}, kStateUnknown}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 5 * NS_PER_SEC);
-
- // Bucket status after screen state change ON->VR.
- // Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_VR);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, ON GROUP}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
- // Value for dimension, state key {{}, kStateUnknown}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 5 * NS_PER_SEC);
-
- // Bucket status after screen state change VR->ON.
- // Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, ON GROUP}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
- // Value for dimension, state key {{}, kStateUnknown}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 5 * NS_PER_SEC);
-
- // Bucket status after screen state change VR->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(screenOffGroup.group_id(),
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, OFF GROUP}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(screenOffGroup.group_id(),
- it->first.getStateValuesKey().getValues()[0].mValue.long_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC);
- // Value for dimension, state key {{}, ON GROUP}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(screenOnGroup.group_id(),
- it->first.getStateValuesKey().getValues()[0].mValue.long_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(16, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 15 * NS_PER_SEC);
- // Value for dimension, state key {{}, kStateUnknown}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
- bucketStartTimeNs + 5 * NS_PER_SEC);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(3, report.value_metrics().data_size());
-
- // {{}, kStateUnknown}
- auto data = report.value_metrics().data(0);
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
- EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{}, ON GROUP}
- data = report.value_metrics().data(1);
- ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
- EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{}, OFF GROUP}
- data = report.value_metrics().data(2);
- ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
- EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
- EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_group_id());
- EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
- EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-}
-
-/*
- * Test metric that slices by state with a primary field and has dimensions
- * - Increasing values
- * - Using diff
- * - Second field is value field
- */
-TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
- // Set up ValueMetricProducer.
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
-
- MetricStateLink* stateLink = metric.add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
-
- /*
- NOTE: "1" denotes uid 1 and "2" denotes uid 2.
- bucket # 1 bucket # 2
- 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
- |------------------------------------------|---------------------------------|--
-
- (kStateUnknown)
- 1
- |-------------|
- 20
-
- 2
- |----------------------------|
- 40
-
- (FOREGROUND)
- 1 1
- |----------------------------|-------------| |------|
- 40 20 10
-
-
- (BACKGROUND)
- 1
- |------------|
- 20
- 2
- |-------------|---------------------------------|
- 20 50
- */
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // ValueMetricProducer initialized.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 3));
- return true;
- }))
- // Uid 1 process state change from kStateUnknown -> Foreground
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
- 1 /*uid*/, 6));
-
- // This event should be skipped.
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
- 2 /*uid*/, 8));
- return true;
- }))
- // Uid 2 process state change from kStateUnknown -> Background
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
- 2 /*uid*/, 9));
-
- // This event should be skipped.
- data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
- 1 /*uid*/, 12));
- return true;
- }))
- // Uid 1 process state change from Foreground -> Background
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
- 1 /*uid*/, 13));
-
- // This event should be skipped.
- data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
- 2 /*uid*/, 11));
- return true;
- }))
- // Uid 1 process state change from Background -> Foreground
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
- 1 /*uid*/, 17));
-
- // This event should be skipped.
- data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
- 2 /*uid */, 15));
- return true;
- }))
- // Dump report pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
- 2 /*uid*/, 20));
- data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
- 1 /*uid*/, 21));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithState(
- pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {});
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-
- // Bucket status after metric initialized.
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {uid 1}.
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{uid 1}, kStateUnknown}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
- // Base for dimension key {uid 2}
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{uid 2}, kStateUnknown}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
-
- // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
- auto uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {uid 1}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 1, kStateUnknown}.
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(3, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
- // Value for key {uid 1, FOREGROUND}.
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Base for dimension key {uid 2}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, kStateUnknown}.
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
-
- // Bucket status after uid 2 process state change kStateUnknown -> Background.
- uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {uid 2}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, BACKGROUND}.
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Base for dimension key {uid 1}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 1, kStateUnknown}.
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(3, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {uid 1, FOREGROUND}.
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {uid 2, kStateUnknown}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC,
- bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Pull at end of first bucket.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 10));
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 15));
- valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
-
- // Buckets flushed after end of first bucket.
- // None of the buckets should have a value.
- ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
- // Base for dimension key {uid 2}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, BACKGROUND}.
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
- EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Base for dimension key {uid 1}
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(10, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 1, kStateUnknown}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Value for key {uid 1, FOREGROUND}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
- EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Value for key {uid 2, kStateUnknown}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
- EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Bucket status after uid 1 process state change from Foreground -> Background.
- uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
-
- ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
- // Base for dimension key {uid 2}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, BACKGROUND}.
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
-
- // Base for dimension key {uid 1}
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(13, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 1, kStateUnknown}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {uid 1, BACKGROUND}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {uid 1, FOREGROUND}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(3, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucket2StartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {uid 2, kStateUnknown}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Bucket status after uid 1 process state change Background->Foreground.
- uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
-
- ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
- // Base for dimension key {uid 2}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, BACKGROUND}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
-
- // Base for dimension key {uid 1}
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(17, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 1, kStateUnknown}
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {uid 1, BACKGROUND}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(4, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucket2StartTimeNs + 40 * NS_PER_SEC);
-
- // Value for key {uid 1, FOREGROUND}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(3, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
- bucket2StartTimeNs + 40 * NS_PER_SEC);
-
- // Value for key {uid 2, kStateUnknown}
- it++;
- ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(5, report.value_metrics().data_size());
-
- // {uid 1, BACKGROUND}
- auto data = report.value_metrics().data(0);
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {uid 2, kStateUnknown}
- data = report.value_metrics().data(1);
- ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
- EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
- EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {uid 1, FOREGROUND}
- data = report.value_metrics().data(2);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size());
- EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
- EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
- EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
-
- // {uid 1, kStateUnknown}
- data = report.value_metrics().data(3);
- ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size());
- EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long());
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {uid 2, BACKGROUND}
- data = report.value_metrics().data(4);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size());
- EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
- EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
-}
-
-/*
- * Test slicing condition_true_nanos by state for metric that slices by state when data is not
- * present in pulled data during a state change.
- */
-TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) {
- // Set up ValueMetricProducer.
- ValueMetric metric =
- ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- /*
- NOTE: "-" means that the data was not present in the pulled data.
-
- bucket # 1
- 10 20 30 40 50 60 (seconds)
- |-------------------------------------------------------|--
- x (kStateUnknown)
- |-----------|
- 10
-
- x x (ON)
- |---------------------| |-----------|
- 20 10
-
- - (OFF)
- */
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // ValueMetricProducer initialized.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- // Battery saver mode state changed to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
- return true;
- }))
- // Battery saver mode state changed to OFF but data for dimension key {} is not present
- // in the pulled data.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
- data->clear();
- return true;
- }))
- // Battery saver mode state changed to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7));
- return true;
- }))
- // Dump report pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithState(
- pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
- EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
- valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
- util::BATTERY_SAVER_MODE_STATE_CHANGED));
-
- // Bucket status after metric initialized.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
-
- // Bucket status after battery saver mode ON event.
- unique_ptr<LogEvent> batterySaverOnEvent =
- CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
-
- // Base for dimension key {}
-
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Bucket status after battery saver mode OFF event which is not present
- // in the pulled data.
- unique_ptr<LogEvent> batterySaverOffEvent =
- CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
-
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_FALSE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucketStartTimeNs + 30 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Bucket status after battery saver mode ON event.
- batterySaverOnEvent =
- CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
-
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
- bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(2, report.value_metrics().data_size());
-
- // {{}, kStateUnknown}
- ValueMetricData data = report.value_metrics().data(0);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{}, ON}
- data = report.value_metrics().data(1);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-}
-
-/*
- * Test for metric that slices by state when data is not present in pulled data
- * during an event and then a flush occurs for the current bucket. With the new
- * condition timer behavior, a "new" MetricDimensionKey is inserted into
- * `mCurrentSlicedBucket` before intervals are closed/added to that new
- * MetricDimensionKey.
- */
-TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) {
- // Set up ValueMetricProducer.
- ValueMetric metric =
- ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- /*
- NOTE: "-" means that the data was not present in the pulled data.
-
- bucket # 1
- 10 20 30 40 50 60 (seconds)
- |-------------------------------------------------------|--
- - (kStateUnknown)
-
- - (ON)
- */
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // ValueMetricProducer initialized but data for dimension key {} is not present
- // in the pulled data..
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- return true;
- }))
- // Battery saver mode state changed to ON but data for dimension key {} is not present
- // in the pulled data.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- return true;
- }))
- // Dump report pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithState(
- pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
- EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
- valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
- util::BATTERY_SAVER_MODE_STATE_CHANGED));
-
- // Bucket status after metric initialized.
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
-
- // Bucket status after battery saver mode ON event which is not present
- // in the pulled data.
- unique_ptr<LogEvent> batterySaverOnEvent =
- CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
-
- ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
-}
-
-TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) {
- // Set up ValueMetricProducer.
- ValueMetric metric =
- ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- /*
- bucket # 1 bucket # 2
- 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
- |------------------------------------|---------------------------|--
- x (kStateUnknown)
- |-----|
- 10
- x x (ON)
- |-----| |-----------|
- 10 20
- x (OFF)
- |------------------------|
- 40
- */
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // ValueMetricProducer initialized.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
- return true;
- }))
- // Battery saver mode state changed to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
- return true;
- }))
- // Battery saver mode state changed to OFF.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7));
- return true;
- }))
- // Battery saver mode state changed to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10));
- return true;
- }))
- // Dump report pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithState(
- pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
- EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
- valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
- util::BATTERY_SAVER_MODE_STATE_CHANGED));
-
- // Bucket status after metric initialized.
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
-
- // Bucket status after battery saver mode ON event.
- unique_ptr<LogEvent> batterySaverOnEvent =
- CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Bucket status after battery saver mode OFF event.
- unique_ptr<LogEvent> batterySaverOffEvent =
- CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, OFF}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{}, ON}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Bucket status after battery saver mode ON event.
- batterySaverOnEvent =
- CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
-
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, OFF}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
- bucket2StartTimeNs + 30 * NS_PER_SEC);
-
- // Value for key {{}, ON}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(3, report.value_metrics().data_size());
-
- // {{}, kStateUnknown}
- ValueMetricData data = report.value_metrics().data(0);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{}, ON}
- data = report.value_metrics().data(1);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
-
- // {{}, OFF}
- data = report.value_metrics().data(2);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-}
-
-/*
- * Test slicing condition_true_nanos by state for metric that slices by state when data is not
- * present in pulled data during a condition change.
- */
-TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) {
- // Set up ValueMetricProducer.
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
- "BATTERY_SAVER_MODE_STATE");
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- /*
- NOTE: "-" means that the data was not present in the pulled data.
-
- bucket # 1
- 10 20 30 40 50 60 (seconds)
- |-------------------------------------------------------|--
-
- T F T (Condition)
- x (ON)
- |----------------------| -
- 20
- */
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Battery saver mode state changed to ON.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3));
- return true;
- }))
- // Condition changed to false.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
- return true;
- }))
- // Condition changed to true but data for dimension key {} is not present in the
- // pulled data.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
- data->clear();
- return true;
- }))
- // Dump report pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
- pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
- ConditionState::kTrue);
- EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
- valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
- util::BATTERY_SAVER_MODE_STATE_CHANGED));
-
- // Bucket status after battery saver mode ON event.
- unique_ptr<LogEvent> batterySaverOnEvent =
- CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket status after condition change to false.
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucketStartTimeNs + 30 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket status after condition change to true.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_FALSE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucketStartTimeNs + 30 * NS_PER_SEC);
-
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(1, report.value_metrics().data_size());
-
- // {{}, ON}
- ValueMetricData data = report.value_metrics().data(0);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-}
-
-/*
- * Test slicing condition_true_nanos by state for metric that slices by state with a primary field,
- * condition, and has multiple dimensions.
- */
-TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
- // Set up ValueMetricProducer.
- ValueMetric metric =
- ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE");
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
- metric.mutable_dimensions_in_what()->add_child()->set_field(3);
-
- MetricStateLink* stateLink = metric.add_state_link();
- stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
- auto fieldsInWhat = stateLink->mutable_fields_in_what();
- *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
- auto fieldsInState = stateLink->mutable_fields_in_state();
- *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
-
- /*
- bucket # 1 bucket # 2
- 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
- |------------------------------------------|---------------------------------|--
-
- T F T (Condition)
- (FOREGROUND)
- x {1, 14}
- |------|
- 10
-
- x {1, 16}
- |------|
- 10
- x {2, 8}
- |-------------|
- 20
-
- (BACKGROUND)
- x {1, 14}
- |-------------| |----------|---------------------------------|
- 20 15 50
-
- x {1, 16}
- |-------------| |----------|---------------------------------|
- 20 15 50
-
- x {2, 8}
- |----------| |----------|-------------------|
- 15 15 30
- */
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Uid 1 process state change from kStateUnknown -> Foreground
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
- 1 /*uid*/, 3, 14 /*tag*/));
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
- 1 /*uid*/, 3, 16 /*tag*/));
-
- // This event should be skipped.
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
- 2 /*uid*/, 5, 8 /*tag*/));
- return true;
- }))
- // Uid 1 process state change from Foreground -> Background
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
- 1 /*uid*/, 5, 14 /*tag*/));
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
- 1 /*uid*/, 5, 16 /*tag*/));
-
- // This event should be skipped.
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
- 2 /*uid*/, 7, 8 /*tag*/));
-
- return true;
- }))
- // Uid 2 process state change from kStateUnknown -> Background
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
- 2 /*uid*/, 9, 8 /*tag*/));
-
- // This event should be skipped.
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
- 1 /*uid*/, 9, 14 /* tag */));
-
- // This event should be skipped.
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
- 1 /*uid*/, 9, 16 /* tag */));
-
- return true;
- }))
- // Condition changed to false.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
- 1 /*uid*/, 11, 14 /* tag */));
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
- 1 /*uid*/, 11, 16 /* tag */));
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
- 2 /*uid*/, 11, 8 /*tag*/));
-
- return true;
- }))
- // Condition changed to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
- 1 /*uid*/, 13, 14 /* tag */));
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
- 1 /*uid*/, 13, 16 /* tag */));
- data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
- 2 /*uid*/, 13, 8 /*tag*/));
- return true;
- }))
- // Uid 2 process state change from Background -> Foreground
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(
- tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
-
- // This event should be skipped.
- data->push_back(CreateThreeValueLogEvent(
- tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
- // This event should be skipped.
- data->push_back(CreateThreeValueLogEvent(
- tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
-
- return true;
- }))
- // Dump report pull.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateThreeValueLogEvent(
- tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */));
- data->push_back(CreateThreeValueLogEvent(
- tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */));
- data->push_back(CreateThreeValueLogEvent(
- tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
- pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue);
- EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
-
- // Condition is true.
- // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
- auto uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension {uid 1, tag 16}.
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, uid 16}, FOREGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
- // Value for key {{uid 1, tag 16}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Base for dimension key {uid 1, tag 14}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, tag 14}, FOREGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
- // Value for key {{uid 1, tag 14}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket status after uid 1 process state change Foreground -> Background.
- uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension {uid 1, tag 16}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, uid 16}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Base for dimension key {uid 1, tag 14}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, tag 14}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, uid 16}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 16}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Value for key {{uid 1, tag 14}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 14}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket status after uid 2 process state change kStateUnknown -> Background.
- uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension {uid 2, tag 8}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 2, uid 8}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC);
-
- // Value for key {{uid 2, uid 8}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Base for dimension {uid 1, tag 16}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, uid 16}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Base for dimension key {uid 1, tag 14}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, tag 14}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, uid 16}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 16}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Value for key {{uid 1, tag 14}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 14}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket 1 status after condition change to false.
- // All condition timers should be turned off.
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
- ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension {uid 2, tag 8}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 2, uid 8}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC,
- bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Value for key {{uid 2, uid 8}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Base for dimension {uid 1, tag 16}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, uid 16}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Base for dimension key {uid 1, tag 14}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, tag 14}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
- bucketStartTimeNs + 40 * NS_PER_SEC);
-
- // Value for key {{uid 1, uid 16}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 16}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Value for key {{uid 1, tag 14}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 14}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket 1 status after condition change to true.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC);
- ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
- ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension {uid 2, tag 8}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 2, uid 8}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC,
- bucketStartTimeNs + 45 * NS_PER_SEC);
-
- // Value for key {{uid 2, uid 8}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Base for dimension {uid 1, tag 16}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, uid 16}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
- bucketStartTimeNs + 45 * NS_PER_SEC);
-
- // Base for dimension key {uid 1, tag 14}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, tag 14}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
- bucketStartTimeNs + 45 * NS_PER_SEC);
-
- // Value for key {{uid 1, uid 16}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 16}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Value for key {{uid 1, tag 14}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 14}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Pull at end of first bucket.
- vector<shared_ptr<LogEvent>> allData;
- allData.push_back(
- CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
- allData.push_back(
- CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
- allData.push_back(
- CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
- valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
-
- // Buckets flushed after end of first bucket.
- // All condition timers' behavior should rollover to bucket 2.
- ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
- ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
- // Base for dimension {uid 2, tag 8}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 2, uid 8}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
- ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
- EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Value for key {{uid 2, uid 8}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Base for dimension {uid 1, tag 16}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, uid 16}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
- ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
- EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Base for dimension key {uid 1, tag 14}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, tag 14}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
- ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
- EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Value for key {{uid 1, uid 16}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
- EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Value for key {{uid 1, tag 16}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Value for key {{uid 1, tag 14}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
- EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
-
- // Value for key {{uid 1, tag 14}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket 2 status after uid 2 process state change Background->Foreground.
- uidProcessEvent =
- CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
- android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
- StateManager::getInstance().onLogEvent(*uidProcessEvent);
-
- ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size());
- ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
- // Base for dimension {uid 2, tag 8}.
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 2, uid 8}, FOREGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
-
- // Value for key {{uid 2, uid 8}, BACKGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
- bucket2StartTimeNs + 30 * NS_PER_SEC);
-
- // Value for key {{uid 2, uid 8}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Base for dimension {uid 1, tag 16}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, uid 16}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
-
- // Base for dimension key {uid 1, tag 14}.
- it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{uid 1, tag 14}, BACKGROUND}.
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
-
- // Value for key {{uid 1, uid 16}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 16}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Value for key {{uid 1, tag 14}, FOREGROUND}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
-
- // Value for key {{uid 1, tag 14}, kStateUnknown}.
- it++;
- ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
- EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(6, report.value_metrics().data_size());
-
- // {{uid 1, tag 14}, FOREGROUND}.
- auto data = report.value_metrics().data(0);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{uid 1, tag 16}, BACKGROUND}.
- data = report.value_metrics().data(1);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
-
- // {{uid 1, tag 14}, BACKGROUND}.
- data = report.value_metrics().data(2);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
-
- // {{uid 1, tag 16}, FOREGROUND}.
- data = report.value_metrics().data(3);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{uid 2, tag 8}, FOREGROUND}.
- data = report.value_metrics().data(4);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- // {{uid 2, tag 8}, BACKGROUND}.
- data = report.value_metrics().data(5);
- EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
- data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
-}
-
-TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
- // Set up ValueMetricProducer.
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
- "BATTERY_SAVER_MODE_STATE");
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
- // Condition changed to true.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3));
- return true;
- }))
- // Battery saver mode state changed to OFF.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
- data->clear();
- data->push_back(
- CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
- return true;
- }))
- // Condition changed to false.
- .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
- data->clear();
- data->push_back(CreateRepeatedValueLogEvent(
- tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 15));
- return true;
- }));
-
- StateManager::getInstance().clear();
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
- pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
- ConditionState::kFalse);
- EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
-
- // Set up StateManager and check that StateTrackers are initialized.
- StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
- valueProducer);
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
- util::BATTERY_SAVER_MODE_STATE_CHANGED));
-
- // Bucket status after battery saver mode ON event.
- // Condition is false so we do nothing.
- unique_ptr<LogEvent> batterySaverOnEvent =
- CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
- EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
-
- // Bucket status after condition change to true.
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- std::unordered_map<HashableDimensionKey, ValueMetricProducer::DimensionsInWhatInfo>::iterator
- itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
- valueProducer->mCurrentSlicedBucket.begin();
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- // Value for key {{}, -1}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket status after battery saver mode OFF event.
- unique_ptr<LogEvent> batterySaverOffEvent =
- CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
- StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, OFF}
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
- // Value for key {{}, ON}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucketStartTimeNs + 30 * NS_PER_SEC);
- // Value for key {{}, -1}
- it++;
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Pull at end of first bucket.
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11));
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(11, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, OFF}
- it = valueProducer->mCurrentSlicedBucket.begin();
- assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
- // Value for key {{}, ON}
- it++;
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
- // Value for key {{}, -1}
- it++;
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Bucket 2 status after condition change to false.
- valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
- // Base for dimension key {}
- ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
- itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
- EXPECT_FALSE(itBase->second.baseInfos[0].hasBase);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, OFF}
- ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
- it = valueProducer->mCurrentSlicedBucket.begin();
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(4, it->second.intervals[0].value.long_value);
- assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
- bucket2StartTimeNs + 10 * NS_PER_SEC);
- // Value for key {{}, ON}
- it++;
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(BatterySaverModeStateChanged::ON,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_FALSE(it->second.intervals[0].hasValue);
- assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
- // Value for key {{}, -1}
- it++;
- assertConditionTimer(it->second.conditionTimer, false, 0, 0);
-
- // Start dump report and check output.
- ProtoOutputStream output;
- std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
- true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
- &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(2, report.value_metrics().data_size());
-
- ValueMetricData data = report.value_metrics().data(0);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
- ASSERT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
-
- data = report.value_metrics().data(1);
- EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
- EXPECT_TRUE(data.slice_by_state(0).has_value());
- EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
- ASSERT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(6, data.bucket_info(0).values(0).value_long());
- EXPECT_EQ(4, data.bucket_info(1).values(0).value_long());
- EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
- EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
-}
-
-/*
- * Test bucket splits when condition is unknown.
- */
-TEST(ValueMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric,
- ConditionState::kUnknown);
-
- // App update event.
- int64_t appUpdateTimeNs = bucketStartTimeNs + 1000;
- valueProducer->notifyAppUpgrade(appUpdateTimeNs);
-
- // Check dump report.
- ProtoOutputStream output;
- std::set<string> strSet;
- int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
- valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
- NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
-
- StatsLogReport report = outputStreamToProto(&output);
- EXPECT_TRUE(report.has_value_metrics());
- ASSERT_EQ(0, report.value_metrics().data_size());
- ASSERT_EQ(1, report.value_metrics().skipped_size());
-
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
- report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
- report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
-
- auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
deleted file mode 100644
index 108df04b45cb..000000000000
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ /dev/null
@@ -1,57 +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.
-
-#include "metrics_test_helper.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) {
- HashableDimensionKey dimension;
- int pos[] = {key, 0, 0};
- dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value)));
-
- return dimension;
-}
-
-HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value) {
- HashableDimensionKey dimension;
- int pos[] = {key, 0, 0};
- dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value)));
-
- return dimension;
-}
-
-MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) {
- return MetricDimensionKey(getMockedDimensionKey(tagId, key, value), DEFAULT_DIMENSION_KEY);
-}
-
-MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value) {
- return MetricDimensionKey(DEFAULT_DIMENSION_KEY,
- getMockedDimensionKeyLongValue(tagId, key, value));
-}
-
-void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) {
- matcher->set_field(tagId);
-}
-
-void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatcher* matcher) {
- matcher->set_field(tagId);
- matcher->add_child()->set_field(fieldNum);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
deleted file mode 100644
index 39232c194ada..000000000000
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ /dev/null
@@ -1,63 +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.
-#pragma once
-
-#include "src/condition/ConditionWizard.h"
-#include "src/external/StatsPullerManager.h"
-#include "src/packages/UidMap.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class MockConditionWizard : public ConditionWizard {
-public:
- MOCK_METHOD3(query,
- ConditionState(const int conditionIndex, const ConditionKey& conditionParameters,
- const bool isPartialLink));
-};
-
-class MockStatsPullerManager : public StatsPullerManager {
-public:
- MOCK_METHOD5(RegisterReceiver,
- void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver,
- int64_t nextPulltimeNs, int64_t intervalNs));
- MOCK_METHOD3(UnRegisterReceiver,
- void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
- MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
- vector<std::shared_ptr<LogEvent>>* data));
- MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids,
- const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data));
- MOCK_METHOD2(RegisterPullUidProvider,
- void(const ConfigKey& configKey, wp<PullUidProvider> provider));
- MOCK_METHOD2(UnregisterPullUidProvider,
- void(const ConfigKey& configKey, wp<PullUidProvider> provider));
-};
-
-HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
-MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
-
-HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value);
-MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value);
-
-// Utils to build FieldMatcher proto for simple one-depth atoms.
-void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher);
-void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
deleted file mode 100644
index d78c14c6ed82..000000000000
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ /dev/null
@@ -1,3632 +0,0 @@
-// Copyright (C) 2020 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 "src/metrics/parsing_utils/config_update_utils.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <private/android_filesystem_config.h>
-#include <stdio.h>
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "src/condition/CombinationConditionTracker.h"
-#include "src/condition/SimpleConditionTracker.h"
-#include "src/matchers/CombinationAtomMatchingTracker.h"
-#include "src/metrics/DurationMetricProducer.h"
-#include "src/metrics/GaugeMetricProducer.h"
-#include "src/metrics/ValueMetricProducer.h"
-#include "src/metrics/parsing_utils/metrics_manager_util.h"
-#include "tests/statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using android::os::statsd::Predicate;
-using std::map;
-using std::nullopt;
-using std::optional;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-ConfigKey key(123, 456);
-const int64_t timeBaseNs = 1000 * NS_PER_SEC;
-
-sp<UidMap> uidMap = new UidMap();
-sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-sp<AlarmMonitor> anomalyAlarmMonitor;
-sp<AlarmMonitor> periodicAlarmMonitor = new AlarmMonitor(
- /*minDiffToUpdateRegisteredAlarmTimeSec=*/0,
- [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
- [](const shared_ptr<IStatsCompanionService>&) {});
-set<int> allTagIds;
-vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers;
-unordered_map<int64_t, int> oldAtomMatchingTrackerMap;
-vector<sp<ConditionTracker>> oldConditionTrackers;
-unordered_map<int64_t, int> oldConditionTrackerMap;
-vector<sp<MetricProducer>> oldMetricProducers;
-unordered_map<int64_t, int> oldMetricProducerMap;
-std::vector<sp<AnomalyTracker>> oldAnomalyTrackers;
-unordered_map<int64_t, int> oldAlertTrackerMap;
-std::vector<sp<AlarmTracker>> oldAlarmTrackers;
-unordered_map<int, std::vector<int>> tmpConditionToMetricMap;
-unordered_map<int, std::vector<int>> tmpTrackerToMetricMap;
-unordered_map<int, std::vector<int>> tmpTrackerToConditionMap;
-unordered_map<int, std::vector<int>> tmpActivationAtomTrackerToMetricMap;
-unordered_map<int, std::vector<int>> tmpDeactivationAtomTrackerToMetricMap;
-vector<int> metricsWithActivation;
-map<int64_t, uint64_t> oldStateHashes;
-std::set<int64_t> noReportMetricIds;
-
-class ConfigUpdateTest : public ::testing::Test {
-public:
- ConfigUpdateTest() {
- }
-
- void SetUp() override {
- allTagIds.clear();
- oldAtomMatchingTrackers.clear();
- oldAtomMatchingTrackerMap.clear();
- oldConditionTrackers.clear();
- oldConditionTrackerMap.clear();
- oldMetricProducers.clear();
- oldMetricProducerMap.clear();
- oldAnomalyTrackers.clear();
- oldAlarmTrackers.clear();
- tmpConditionToMetricMap.clear();
- tmpTrackerToMetricMap.clear();
- tmpTrackerToConditionMap.clear();
- tmpActivationAtomTrackerToMetricMap.clear();
- tmpDeactivationAtomTrackerToMetricMap.clear();
- oldAlertTrackerMap.clear();
- metricsWithActivation.clear();
- oldStateHashes.clear();
- noReportMetricIds.clear();
- StateManager::getInstance().clear();
- }
-};
-
-bool initConfig(const StatsdConfig& config) {
- return initStatsdConfig(
- key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap,
- oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap,
- oldAnomalyTrackers, oldAlarmTrackers, tmpConditionToMetricMap, tmpTrackerToMetricMap,
- tmpTrackerToConditionMap, tmpActivationAtomTrackerToMetricMap,
- tmpDeactivationAtomTrackerToMetricMap, oldAlertTrackerMap, metricsWithActivation,
- oldStateHashes, noReportMetricIds);
-}
-
-EventMetric createEventMetric(string name, int64_t what, optional<int64_t> condition) {
- EventMetric metric;
- metric.set_id(StringToId(name));
- metric.set_what(what);
- if (condition) {
- metric.set_condition(condition.value());
- }
- return metric;
-}
-
-CountMetric createCountMetric(string name, int64_t what, optional<int64_t> condition,
- vector<int64_t> states) {
- CountMetric metric;
- metric.set_id(StringToId(name));
- metric.set_what(what);
- metric.set_bucket(TEN_MINUTES);
- if (condition) {
- metric.set_condition(condition.value());
- }
- for (const int64_t state : states) {
- metric.add_slice_by_state(state);
- }
- return metric;
-}
-
-GaugeMetric createGaugeMetric(string name, int64_t what, GaugeMetric::SamplingType samplingType,
- optional<int64_t> condition, optional<int64_t> triggerEvent) {
- GaugeMetric metric;
- metric.set_id(StringToId(name));
- metric.set_what(what);
- metric.set_bucket(TEN_MINUTES);
- metric.set_sampling_type(samplingType);
- if (condition) {
- metric.set_condition(condition.value());
- }
- if (triggerEvent) {
- metric.set_trigger_event(triggerEvent.value());
- }
- metric.mutable_gauge_fields_filter()->set_include_all(true);
- return metric;
-}
-
-DurationMetric createDurationMetric(string name, int64_t what, optional<int64_t> condition,
- vector<int64_t> states) {
- DurationMetric metric;
- metric.set_id(StringToId(name));
- metric.set_what(what);
- metric.set_bucket(TEN_MINUTES);
- if (condition) {
- metric.set_condition(condition.value());
- }
- for (const int64_t state : states) {
- metric.add_slice_by_state(state);
- }
- return metric;
-}
-
-ValueMetric createValueMetric(string name, const AtomMatcher& what, optional<int64_t> condition,
- vector<int64_t> states) {
- ValueMetric metric;
- metric.set_id(StringToId(name));
- metric.set_what(what.id());
- metric.set_bucket(TEN_MINUTES);
- metric.mutable_value_field()->set_field(what.simple_atom_matcher().atom_id());
- metric.mutable_value_field()->add_child()->set_field(2);
- if (condition) {
- metric.set_condition(condition.value());
- }
- for (const int64_t state : states) {
- metric.add_slice_by_state(state);
- }
- return metric;
-}
-
-Alert createAlert(string name, int64_t metricId, int buckets, int64_t triggerSum) {
- Alert alert;
- alert.set_id(StringToId(name));
- alert.set_metric_id(metricId);
- alert.set_num_buckets(buckets);
- alert.set_trigger_if_sum_gt(triggerSum);
- return alert;
-}
-
-Subscription createSubscription(string name, Subscription_RuleType type, int64_t ruleId) {
- Subscription subscription;
- subscription.set_id(StringToId(name));
- subscription.set_rule_type(type);
- subscription.set_rule_id(ruleId);
- subscription.mutable_broadcast_subscriber_details();
- return subscription;
-}
-
-Alarm createAlarm(string name, int64_t offsetMillis, int64_t periodMillis) {
- Alarm alarm;
- alarm.set_id(StringToId(name));
- alarm.set_offset_millis(offsetMillis);
- alarm.set_period_millis(periodMillis);
- return alarm;
-}
-} // anonymous namespace
-
-TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
- StatsdConfig config;
- AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
- int64_t matcherId = matcher.id();
- *config.add_atom_matcher() = matcher;
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- newAtomMatchingTrackerMap[matcherId] = 0;
- EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
- matchersToUpdate, cycleTracker));
- EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) {
- StatsdConfig config;
- AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
- *config.add_atom_matcher() = matcher;
-
- EXPECT_TRUE(initConfig(config));
-
- StatsdConfig newConfig;
- // Same id, different atom, so should be replaced.
- AtomMatcher newMatcher = CreateSimpleAtomMatcher("TEST", /*atom=*/11);
- int64_t matcherId = newMatcher.id();
- EXPECT_EQ(matcherId, matcher.id());
- *newConfig.add_atom_matcher() = newMatcher;
-
- vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- newAtomMatchingTrackerMap[matcherId] = 0;
- EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
- matchersToUpdate, cycleTracker));
- EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) {
- StatsdConfig config;
- AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
- *config.add_atom_matcher() = matcher;
-
- EXPECT_TRUE(initConfig(config));
-
- StatsdConfig newConfig;
- // Different id, so should be a new matcher.
- AtomMatcher newMatcher = CreateSimpleAtomMatcher("DIFFERENT_NAME", /*atom=*/10);
- int64_t matcherId = newMatcher.id();
- EXPECT_NE(matcherId, matcher.id());
- *newConfig.add_atom_matcher() = newMatcher;
-
- vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- newAtomMatchingTrackerMap[matcherId] = 0;
- EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
- matchersToUpdate, cycleTracker));
- EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW);
-}
-
-TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) {
- StatsdConfig config;
- AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
- *config.add_atom_matcher() = matcher2;
- int64_t matcher2Id = matcher2.id();
-
- AtomMatcher matcher3;
- matcher3.set_id(StringToId("TEST3"));
- AtomMatcher_Combination* combination = matcher3.mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(matcher1Id);
- combination->add_matcher(matcher2Id);
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- EXPECT_TRUE(initConfig(config));
-
- StatsdConfig newConfig;
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- // Same matchers, different order, all should be preserved.
- *newConfig.add_atom_matcher() = matcher2;
- newAtomMatchingTrackerMap[matcher2Id] = 0;
- *newConfig.add_atom_matcher() = matcher3;
- newAtomMatchingTrackerMap[matcher3Id] = 1;
- *newConfig.add_atom_matcher() = matcher1;
- newAtomMatchingTrackerMap[matcher1Id] = 2;
-
- vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
- // Only update the combination. It should recurse the two child matchers and preserve all 3.
- EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
- matchersToUpdate, cycleTracker));
- EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
- EXPECT_EQ(matchersToUpdate[1], UPDATE_PRESERVE);
- EXPECT_EQ(matchersToUpdate[2], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestCombinationMatcherReplace) {
- StatsdConfig config;
- AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
- *config.add_atom_matcher() = matcher2;
- int64_t matcher2Id = matcher2.id();
-
- AtomMatcher matcher3;
- matcher3.set_id(StringToId("TEST3"));
- AtomMatcher_Combination* combination = matcher3.mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(matcher1Id);
- combination->add_matcher(matcher2Id);
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- EXPECT_TRUE(initConfig(config));
-
- // Change the logical operation of the combination matcher, causing a replacement.
- matcher3.mutable_combination()->set_operation(LogicalOperation::AND);
-
- StatsdConfig newConfig;
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- *newConfig.add_atom_matcher() = matcher2;
- newAtomMatchingTrackerMap[matcher2Id] = 0;
- *newConfig.add_atom_matcher() = matcher3;
- newAtomMatchingTrackerMap[matcher3Id] = 1;
- *newConfig.add_atom_matcher() = matcher1;
- newAtomMatchingTrackerMap[matcher1Id] = 2;
-
- vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
- // Only update the combination. The simple matchers should not be evaluated.
- EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
- matchersToUpdate, cycleTracker));
- EXPECT_EQ(matchersToUpdate[0], UPDATE_UNKNOWN);
- EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
- EXPECT_EQ(matchersToUpdate[2], UPDATE_UNKNOWN);
-}
-
-TEST_F(ConfigUpdateTest, TestCombinationMatcherDepsChange) {
- StatsdConfig config;
- AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
- *config.add_atom_matcher() = matcher2;
- int64_t matcher2Id = matcher2.id();
-
- AtomMatcher matcher3;
- matcher3.set_id(StringToId("TEST3"));
- AtomMatcher_Combination* combination = matcher3.mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(matcher1Id);
- combination->add_matcher(matcher2Id);
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- EXPECT_TRUE(initConfig(config));
-
- // Change a dependency of matcher 3.
- matcher2.mutable_simple_atom_matcher()->set_atom_id(12);
-
- StatsdConfig newConfig;
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- *newConfig.add_atom_matcher() = matcher2;
- newAtomMatchingTrackerMap[matcher2Id] = 0;
- *newConfig.add_atom_matcher() = matcher3;
- newAtomMatchingTrackerMap[matcher3Id] = 1;
- *newConfig.add_atom_matcher() = matcher1;
- newAtomMatchingTrackerMap[matcher1Id] = 2;
-
- vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
- // Only update the combination.
- EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
- oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
- matchersToUpdate, cycleTracker));
- // Matcher 2 and matcher3 must be reevaluated. Matcher 1 might, but does not need to be.
- EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
- EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateMatchers) {
- StatsdConfig config;
- // Will be preserved.
- AtomMatcher simple1 = CreateSimpleAtomMatcher("SIMPLE1", /*atom=*/10);
- int64_t simple1Id = simple1.id();
- *config.add_atom_matcher() = simple1;
-
- // Will be replaced.
- AtomMatcher simple2 = CreateSimpleAtomMatcher("SIMPLE2", /*atom=*/11);
- *config.add_atom_matcher() = simple2;
- int64_t simple2Id = simple2.id();
-
- // Will be removed.
- AtomMatcher simple3 = CreateSimpleAtomMatcher("SIMPLE3", /*atom=*/12);
- *config.add_atom_matcher() = simple3;
- int64_t simple3Id = simple3.id();
-
- // Will be preserved.
- AtomMatcher combination1;
- combination1.set_id(StringToId("combination1"));
- AtomMatcher_Combination* combination = combination1.mutable_combination();
- combination->set_operation(LogicalOperation::NOT);
- combination->add_matcher(simple1Id);
- int64_t combination1Id = combination1.id();
- *config.add_atom_matcher() = combination1;
-
- // Will be replaced since it depends on simple2.
- AtomMatcher combination2;
- combination2.set_id(StringToId("combination2"));
- combination = combination2.mutable_combination();
- combination->set_operation(LogicalOperation::AND);
- combination->add_matcher(simple1Id);
- combination->add_matcher(simple2Id);
- int64_t combination2Id = combination2.id();
- *config.add_atom_matcher() = combination2;
-
- EXPECT_TRUE(initConfig(config));
-
- // Change simple2, causing simple2 and combination2 to be replaced.
- simple2.mutable_simple_atom_matcher()->set_atom_id(111);
-
- // 2 new matchers: simple4 and combination3:
- AtomMatcher simple4 = CreateSimpleAtomMatcher("SIMPLE4", /*atom=*/13);
- int64_t simple4Id = simple4.id();
-
- AtomMatcher combination3;
- combination3.set_id(StringToId("combination3"));
- combination = combination3.mutable_combination();
- combination->set_operation(LogicalOperation::AND);
- combination->add_matcher(simple4Id);
- combination->add_matcher(simple2Id);
- int64_t combination3Id = combination3.id();
-
- StatsdConfig newConfig;
- *newConfig.add_atom_matcher() = combination3;
- *newConfig.add_atom_matcher() = simple2;
- *newConfig.add_atom_matcher() = combination2;
- *newConfig.add_atom_matcher() = simple1;
- *newConfig.add_atom_matcher() = simple4;
- *newConfig.add_atom_matcher() = combination1;
-
- set<int> newTagIds;
- unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
- set<int64_t> replacedMatchers;
- EXPECT_TRUE(updateAtomMatchingTrackers(
- newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds,
- newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers));
-
- ASSERT_EQ(newTagIds.size(), 3);
- EXPECT_EQ(newTagIds.count(10), 1);
- EXPECT_EQ(newTagIds.count(111), 1);
- EXPECT_EQ(newTagIds.count(13), 1);
-
- ASSERT_EQ(newAtomMatchingTrackerMap.size(), 6);
- EXPECT_EQ(newAtomMatchingTrackerMap.at(combination3Id), 0);
- EXPECT_EQ(newAtomMatchingTrackerMap.at(simple2Id), 1);
- EXPECT_EQ(newAtomMatchingTrackerMap.at(combination2Id), 2);
- EXPECT_EQ(newAtomMatchingTrackerMap.at(simple1Id), 3);
- EXPECT_EQ(newAtomMatchingTrackerMap.at(simple4Id), 4);
- EXPECT_EQ(newAtomMatchingTrackerMap.at(combination1Id), 5);
-
- ASSERT_EQ(newAtomMatchingTrackers.size(), 6);
- // Make sure all atom matchers are initialized:
- for (const sp<AtomMatchingTracker>& tracker : newAtomMatchingTrackers) {
- EXPECT_TRUE(tracker->mInitialized);
- }
- // Make sure preserved atom matchers are the same.
- EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple1Id)],
- newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple1Id)]);
- EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination1Id)],
- newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination1Id)]);
- // Make sure replaced matchers are different.
- EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple2Id)],
- newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple2Id)]);
- EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)],
- newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]);
-
- // Validation, make sure the matchers have the proper ids/indices. Could do more checks here.
- EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id);
- EXPECT_EQ(newAtomMatchingTrackers[0]->mIndex, 0);
- EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id);
- EXPECT_EQ(newAtomMatchingTrackers[1]->mIndex, 1);
- EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id);
- EXPECT_EQ(newAtomMatchingTrackers[2]->mIndex, 2);
- EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id);
- EXPECT_EQ(newAtomMatchingTrackers[3]->mIndex, 3);
- EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id);
- EXPECT_EQ(newAtomMatchingTrackers[4]->mIndex, 4);
- EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id);
- EXPECT_EQ(newAtomMatchingTrackers[5]->mIndex, 5);
-
- // Verify child indices of Combination Matchers are correct.
- CombinationAtomMatchingTracker* combinationTracker1 =
- static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[5].get());
- vector<int>* childMatchers = &combinationTracker1->mChildren;
- EXPECT_EQ(childMatchers->size(), 1);
- EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
-
- CombinationAtomMatchingTracker* combinationTracker2 =
- static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[2].get());
- childMatchers = &combinationTracker2->mChildren;
- EXPECT_EQ(childMatchers->size(), 2);
- EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
- EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
-
- CombinationAtomMatchingTracker* combinationTracker3 =
- static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[0].get());
- childMatchers = &combinationTracker3->mChildren;
- EXPECT_EQ(childMatchers->size(), 2);
- EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
- EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end());
-
- // Expect replacedMatchers to have simple2 and combination2
- ASSERT_EQ(replacedMatchers.size(), 2);
- EXPECT_NE(replacedMatchers.find(simple2Id), replacedMatchers.end());
- EXPECT_NE(replacedMatchers.find(combination2Id), replacedMatchers.end());
-}
-
-TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- set<int64_t> replacedMatchers;
- vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
- unordered_map<int64_t, int> newConditionTrackerMap;
- newConditionTrackerMap[predicate.id()] = 0;
- EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
- oldConditionTrackers, newConditionTrackerMap,
- replacedMatchers, conditionsToUpdate, cycleTracker));
- EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- EXPECT_TRUE(initConfig(config));
-
- // Modify the predicate.
- config.mutable_predicate(0)->mutable_simple_predicate()->set_count_nesting(true);
-
- set<int64_t> replacedMatchers;
- vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
- unordered_map<int64_t, int> newConditionTrackerMap;
- newConditionTrackerMap[predicate.id()] = 0;
- EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
- oldConditionTrackers, newConditionTrackerMap,
- replacedMatchers, conditionsToUpdate, cycleTracker));
- EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- int64_t startMatcherId = startMatcher.id();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- EXPECT_TRUE(initConfig(config));
-
- // Start matcher was replaced.
- set<int64_t> replacedMatchers;
- replacedMatchers.insert(startMatcherId);
-
- vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(1, false);
- unordered_map<int64_t, int> newConditionTrackerMap;
- newConditionTrackerMap[predicate.id()] = 0;
- EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
- oldConditionTrackers, newConditionTrackerMap,
- replacedMatchers, conditionsToUpdate, cycleTracker));
- EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) {
- StatsdConfig config;
- AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = screenOnMatcher;
- AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = screenOffMatcher;
-
- Predicate simple1 = CreateScreenIsOnPredicate();
- *config.add_predicate() = simple1;
- Predicate simple2 = CreateScreenIsOffPredicate();
- *config.add_predicate() = simple2;
-
- Predicate combination1;
- combination1.set_id(StringToId("COMBINATION1"));
- Predicate_Combination* combinationInternal = combination1.mutable_combination();
- combinationInternal->set_operation(LogicalOperation::NAND);
- combinationInternal->add_predicate(simple1.id());
- combinationInternal->add_predicate(simple2.id());
- *config.add_predicate() = combination1;
-
- EXPECT_TRUE(initConfig(config));
-
- // Same predicates, different order
- StatsdConfig newConfig;
- unordered_map<int64_t, int> newConditionTrackerMap;
- *newConfig.add_predicate() = combination1;
- newConditionTrackerMap[combination1.id()] = 0;
- *newConfig.add_predicate() = simple2;
- newConditionTrackerMap[simple2.id()] = 1;
- *newConfig.add_predicate() = simple1;
- newConditionTrackerMap[simple1.id()] = 2;
-
- set<int64_t> replacedMatchers;
- vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
- // Only update the combination. It should recurse the two child predicates and preserve all 3.
- EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
- oldConditionTrackers, newConditionTrackerMap,
- replacedMatchers, conditionsToUpdate, cycleTracker));
- EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
- EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE);
- EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) {
- StatsdConfig config;
- AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = screenOnMatcher;
- AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = screenOffMatcher;
-
- Predicate simple1 = CreateScreenIsOnPredicate();
- *config.add_predicate() = simple1;
- Predicate simple2 = CreateScreenIsOffPredicate();
- *config.add_predicate() = simple2;
-
- Predicate combination1;
- combination1.set_id(StringToId("COMBINATION1"));
- Predicate_Combination* combinationInternal = combination1.mutable_combination();
- combinationInternal->set_operation(LogicalOperation::NAND);
- combinationInternal->add_predicate(simple1.id());
- combinationInternal->add_predicate(simple2.id());
- *config.add_predicate() = combination1;
-
- EXPECT_TRUE(initConfig(config));
-
- // Changing the logical operation changes the predicate definition, so it should be replaced.
- combination1.mutable_combination()->set_operation(LogicalOperation::OR);
-
- StatsdConfig newConfig;
- unordered_map<int64_t, int> newConditionTrackerMap;
- *newConfig.add_predicate() = combination1;
- newConditionTrackerMap[combination1.id()] = 0;
- *newConfig.add_predicate() = simple2;
- newConditionTrackerMap[simple2.id()] = 1;
- *newConfig.add_predicate() = simple1;
- newConditionTrackerMap[simple1.id()] = 2;
-
- set<int64_t> replacedMatchers;
- vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
- // Only update the combination. The simple conditions should not be evaluated.
- EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
- oldConditionTrackers, newConditionTrackerMap,
- replacedMatchers, conditionsToUpdate, cycleTracker));
- EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
- EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN);
- EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN);
-}
-
-TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) {
- StatsdConfig config;
- AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = screenOnMatcher;
- AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = screenOffMatcher;
-
- Predicate simple1 = CreateScreenIsOnPredicate();
- *config.add_predicate() = simple1;
- Predicate simple2 = CreateScreenIsOffPredicate();
- *config.add_predicate() = simple2;
-
- Predicate combination1;
- combination1.set_id(StringToId("COMBINATION1"));
- Predicate_Combination* combinationInternal = combination1.mutable_combination();
- combinationInternal->set_operation(LogicalOperation::NAND);
- combinationInternal->add_predicate(simple1.id());
- combinationInternal->add_predicate(simple2.id());
- *config.add_predicate() = combination1;
-
- EXPECT_TRUE(initConfig(config));
-
- simple2.mutable_simple_predicate()->set_count_nesting(false);
-
- StatsdConfig newConfig;
- unordered_map<int64_t, int> newConditionTrackerMap;
- *newConfig.add_predicate() = combination1;
- newConditionTrackerMap[combination1.id()] = 0;
- *newConfig.add_predicate() = simple2;
- newConditionTrackerMap[simple2.id()] = 1;
- *newConfig.add_predicate() = simple1;
- newConditionTrackerMap[simple1.id()] = 2;
-
- set<int64_t> replacedMatchers;
- vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
- vector<bool> cycleTracker(3, false);
- // Only update the combination. Simple2 and combination1 must be evaluated.
- EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
- oldConditionTrackers, newConditionTrackerMap,
- replacedMatchers, conditionsToUpdate, cycleTracker));
- EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
- EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateConditions) {
- StatsdConfig config;
- // Add atom matchers. These are mostly needed for initStatsdConfig
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
- int64_t matcher4Id = matcher4.id();
- *config.add_atom_matcher() = matcher4;
-
- AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
- int64_t matcher5Id = matcher5.id();
- *config.add_atom_matcher() = matcher5;
-
- AtomMatcher matcher6 = CreateBatterySaverModeStopAtomMatcher();
- int64_t matcher6Id = matcher6.id();
- *config.add_atom_matcher() = matcher6;
-
- // Add the predicates.
- // Will be preserved.
- Predicate simple1 = CreateScreenIsOnPredicate();
- int64_t simple1Id = simple1.id();
- *config.add_predicate() = simple1;
-
- // Will be preserved.
- Predicate simple2 = CreateScheduledJobPredicate();
- int64_t simple2Id = simple2.id();
- *config.add_predicate() = simple2;
-
- // Will be replaced.
- Predicate simple3 = CreateBatterySaverModePredicate();
- int64_t simple3Id = simple3.id();
- *config.add_predicate() = simple3;
-
- // Will be preserved
- Predicate combination1;
- combination1.set_id(StringToId("COMBINATION1"));
- combination1.mutable_combination()->set_operation(LogicalOperation::AND);
- combination1.mutable_combination()->add_predicate(simple1Id);
- combination1.mutable_combination()->add_predicate(simple2Id);
- int64_t combination1Id = combination1.id();
- *config.add_predicate() = combination1;
-
- // Will be replaced since simple3 will be replaced.
- Predicate combination2;
- combination2.set_id(StringToId("COMBINATION2"));
- combination2.mutable_combination()->set_operation(LogicalOperation::OR);
- combination2.mutable_combination()->add_predicate(simple1Id);
- combination2.mutable_combination()->add_predicate(simple3Id);
- int64_t combination2Id = combination2.id();
- *config.add_predicate() = combination2;
-
- // Will be removed.
- Predicate combination3;
- combination3.set_id(StringToId("COMBINATION3"));
- combination3.mutable_combination()->set_operation(LogicalOperation::NOT);
- combination3.mutable_combination()->add_predicate(simple2Id);
- int64_t combination3Id = combination3.id();
- *config.add_predicate() = combination3;
-
- EXPECT_TRUE(initConfig(config));
-
- // Mark marcher 5 as replaced. Causes simple3, and therefore combination2 to be replaced.
- set<int64_t> replacedMatchers;
- replacedMatchers.insert(matcher6Id);
-
- // Change the condition of simple1 to false.
- ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id);
- LogEvent event(/*uid=*/0, /*pid=*/0); // Empty event is fine since there are no dimensions.
- // Mark the stop matcher as matched, condition should be false.
- vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched);
- eventMatcherValues[1] = MatchingState::kMatched;
- vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated);
- vector<bool> conditionChangeCache(6, false);
- oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers,
- tmpConditionCache, conditionChangeCache);
- EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse);
- EXPECT_EQ(conditionChangeCache[0], true);
-
- // New combination predicate. Should have an initial condition of true since it is NOT(simple1).
- Predicate combination4;
- combination4.set_id(StringToId("COMBINATION4"));
- combination4.mutable_combination()->set_operation(LogicalOperation::NOT);
- combination4.mutable_combination()->add_predicate(simple1Id);
- int64_t combination4Id = combination4.id();
- *config.add_predicate() = combination4;
-
- // Map the matchers in reverse order to force the indices to change.
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher6Index = 0;
- newAtomMatchingTrackerMap[matcher6Id] = 0;
- const int matcher5Index = 1;
- newAtomMatchingTrackerMap[matcher5Id] = 1;
- const int matcher4Index = 2;
- newAtomMatchingTrackerMap[matcher4Id] = 2;
- const int matcher3Index = 3;
- newAtomMatchingTrackerMap[matcher3Id] = 3;
- const int matcher2Index = 4;
- newAtomMatchingTrackerMap[matcher2Id] = 4;
- const int matcher1Index = 5;
- newAtomMatchingTrackerMap[matcher1Id] = 5;
-
- StatsdConfig newConfig;
- *newConfig.add_predicate() = simple3;
- const int simple3Index = 0;
- *newConfig.add_predicate() = combination2;
- const int combination2Index = 1;
- *newConfig.add_predicate() = combination4;
- const int combination4Index = 2;
- *newConfig.add_predicate() = simple2;
- const int simple2Index = 3;
- *newConfig.add_predicate() = combination1;
- const int combination1Index = 4;
- *newConfig.add_predicate() = simple1;
- const int simple1Index = 5;
-
- unordered_map<int64_t, int> newConditionTrackerMap;
- vector<sp<ConditionTracker>> newConditionTrackers;
- unordered_map<int, vector<int>> trackerToConditionMap;
- std::vector<ConditionState> conditionCache;
- std::set<int64_t> replacedConditions;
- EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers,
- oldConditionTrackerMap, oldConditionTrackers,
- newConditionTrackerMap, newConditionTrackers,
- trackerToConditionMap, conditionCache, replacedConditions));
-
- unordered_map<int64_t, int> expectedConditionTrackerMap = {
- {simple1Id, simple1Index}, {simple2Id, simple2Index},
- {simple3Id, simple3Index}, {combination1Id, combination1Index},
- {combination2Id, combination2Index}, {combination4Id, combination4Index},
- };
- EXPECT_THAT(newConditionTrackerMap, ContainerEq(expectedConditionTrackerMap));
-
- ASSERT_EQ(newConditionTrackers.size(), 6);
- // Make sure all conditions are initialized:
- for (const sp<ConditionTracker>& tracker : newConditionTrackers) {
- EXPECT_TRUE(tracker->mInitialized);
- }
-
- // Make sure preserved conditions are the same.
- EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple1Id)],
- newConditionTrackers[newConditionTrackerMap.at(simple1Id)]);
- EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple2Id)],
- newConditionTrackers[newConditionTrackerMap.at(simple2Id)]);
- EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(combination1Id)],
- newConditionTrackers[newConditionTrackerMap.at(combination1Id)]);
-
- // Make sure replaced conditions are different and included in replacedConditions.
- EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(simple3Id)],
- newConditionTrackers[newConditionTrackerMap.at(simple3Id)]);
- EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(combination2Id)],
- newConditionTrackers[newConditionTrackerMap.at(combination2Id)]);
- EXPECT_THAT(replacedConditions, ContainerEq(set({simple3Id, combination2Id})));
-
- // Verify the trackerToConditionMap
- ASSERT_EQ(trackerToConditionMap.size(), 6);
- const vector<int>& matcher1Conditions = trackerToConditionMap[matcher1Index];
- EXPECT_THAT(matcher1Conditions, UnorderedElementsAre(simple1Index, combination1Index,
- combination2Index, combination4Index));
- const vector<int>& matcher2Conditions = trackerToConditionMap[matcher2Index];
- EXPECT_THAT(matcher2Conditions, UnorderedElementsAre(simple1Index, combination1Index,
- combination2Index, combination4Index));
- const vector<int>& matcher3Conditions = trackerToConditionMap[matcher3Index];
- EXPECT_THAT(matcher3Conditions, UnorderedElementsAre(simple2Index, combination1Index));
- const vector<int>& matcher4Conditions = trackerToConditionMap[matcher4Index];
- EXPECT_THAT(matcher4Conditions, UnorderedElementsAre(simple2Index, combination1Index));
- const vector<int>& matcher5Conditions = trackerToConditionMap[matcher5Index];
- EXPECT_THAT(matcher5Conditions, UnorderedElementsAre(simple3Index, combination2Index));
- const vector<int>& matcher6Conditions = trackerToConditionMap[matcher6Index];
- EXPECT_THAT(matcher6Conditions, UnorderedElementsAre(simple3Index, combination2Index));
-
- // Verify the conditionCache. Specifically, simple1 is false and combination4 is true.
- ASSERT_EQ(conditionCache.size(), 6);
- EXPECT_EQ(conditionCache[simple1Index], ConditionState::kFalse);
- EXPECT_EQ(conditionCache[simple2Index], ConditionState::kUnknown);
- EXPECT_EQ(conditionCache[simple3Index], ConditionState::kUnknown);
- EXPECT_EQ(conditionCache[combination1Index], ConditionState::kUnknown);
- EXPECT_EQ(conditionCache[combination2Index], ConditionState::kUnknown);
- EXPECT_EQ(conditionCache[combination4Index], ConditionState::kTrue);
-
- // Verify tracker indices/ids are correct.
- EXPECT_EQ(newConditionTrackers[simple1Index]->getConditionId(), simple1Id);
- EXPECT_EQ(newConditionTrackers[simple1Index]->mIndex, simple1Index);
- EXPECT_TRUE(newConditionTrackers[simple1Index]->IsSimpleCondition());
- EXPECT_EQ(newConditionTrackers[simple2Index]->getConditionId(), simple2Id);
- EXPECT_EQ(newConditionTrackers[simple2Index]->mIndex, simple2Index);
- EXPECT_TRUE(newConditionTrackers[simple2Index]->IsSimpleCondition());
- EXPECT_EQ(newConditionTrackers[simple3Index]->getConditionId(), simple3Id);
- EXPECT_EQ(newConditionTrackers[simple3Index]->mIndex, simple3Index);
- EXPECT_TRUE(newConditionTrackers[simple3Index]->IsSimpleCondition());
- EXPECT_EQ(newConditionTrackers[combination1Index]->getConditionId(), combination1Id);
- EXPECT_EQ(newConditionTrackers[combination1Index]->mIndex, combination1Index);
- EXPECT_FALSE(newConditionTrackers[combination1Index]->IsSimpleCondition());
- EXPECT_EQ(newConditionTrackers[combination2Index]->getConditionId(), combination2Id);
- EXPECT_EQ(newConditionTrackers[combination2Index]->mIndex, combination2Index);
- EXPECT_FALSE(newConditionTrackers[combination2Index]->IsSimpleCondition());
- EXPECT_EQ(newConditionTrackers[combination4Index]->getConditionId(), combination4Id);
- EXPECT_EQ(newConditionTrackers[combination4Index]->mIndex, combination4Index);
- EXPECT_FALSE(newConditionTrackers[combination4Index]->IsSimpleCondition());
-
- // Verify preserved trackers have indices updated.
- SimpleConditionTracker* simpleTracker1 =
- static_cast<SimpleConditionTracker*>(newConditionTrackers[simple1Index].get());
- EXPECT_EQ(simpleTracker1->mStartLogMatcherIndex, matcher1Index);
- EXPECT_EQ(simpleTracker1->mStopLogMatcherIndex, matcher2Index);
- EXPECT_EQ(simpleTracker1->mStopAllLogMatcherIndex, -1);
-
- SimpleConditionTracker* simpleTracker2 =
- static_cast<SimpleConditionTracker*>(newConditionTrackers[simple2Index].get());
- EXPECT_EQ(simpleTracker2->mStartLogMatcherIndex, matcher3Index);
- EXPECT_EQ(simpleTracker2->mStopLogMatcherIndex, matcher4Index);
- EXPECT_EQ(simpleTracker2->mStopAllLogMatcherIndex, -1);
-
- CombinationConditionTracker* combinationTracker1 = static_cast<CombinationConditionTracker*>(
- newConditionTrackers[combination1Index].get());
- EXPECT_THAT(combinationTracker1->mChildren, UnorderedElementsAre(simple1Index, simple2Index));
- EXPECT_THAT(combinationTracker1->mUnSlicedChildren,
- UnorderedElementsAre(simple1Index, simple2Index));
- EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty());
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateStates) {
- StatsdConfig config;
- // Add states.
- // Will be replaced because we add a state map.
- State state1 = CreateScreenState();
- int64_t state1Id = state1.id();
- *config.add_state() = state1;
-
- // Will be preserved.
- State state2 = CreateUidProcessState();
- int64_t state2Id = state2.id();
- *config.add_state() = state2;
-
- // Will be replaced since the atom changes from overlay to screen.
- State state3 = CreateOverlayState();
- int64_t state3Id = state3.id();
- *config.add_state() = state3;
-
- EXPECT_TRUE(initConfig(config));
-
- // Change definitions of state1 and state3.
- int64_t screenOnId = 0x4321, screenOffId = 0x1234;
- *state1.mutable_map() = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId);
- state3.set_atom_id(util::SCREEN_STATE_CHANGED);
-
- StatsdConfig newConfig;
- *newConfig.add_state() = state3;
- *newConfig.add_state() = state1;
- *newConfig.add_state() = state2;
-
- unordered_map<int64_t, int> stateAtomIdMap;
- unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
- map<int64_t, uint64_t> newStateProtoHashes;
- set<int64_t> replacedStates;
- EXPECT_TRUE(updateStates(newConfig, oldStateHashes, stateAtomIdMap, allStateGroupMaps,
- newStateProtoHashes, replacedStates));
- EXPECT_THAT(replacedStates, ContainerEq(set({state1Id, state3Id})));
-
- unordered_map<int64_t, int> expectedStateAtomIdMap = {
- {state1Id, util::SCREEN_STATE_CHANGED},
- {state2Id, util::UID_PROCESS_STATE_CHANGED},
- {state3Id, util::SCREEN_STATE_CHANGED}};
- EXPECT_THAT(stateAtomIdMap, ContainerEq(expectedStateAtomIdMap));
-
- unordered_map<int64_t, unordered_map<int, int64_t>> expectedStateGroupMaps = {
- {state1Id,
- {{android::view::DisplayStateEnum::DISPLAY_STATE_OFF, screenOffId},
- {android::view::DisplayStateEnum::DISPLAY_STATE_ON, screenOnId}}}};
- EXPECT_THAT(allStateGroupMaps, ContainerEq(expectedStateGroupMaps));
-}
-
-TEST_F(ConfigUpdateTest, TestEventMetricPreserve) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- EventMetric* metric = config.add_event_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- EventMetric* metric = config.add_event_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- // Add a metric activation, which should change the proto, causing replacement.
- MetricActivation* activation = config.add_metric_activation();
- activation->set_metric_id(12345);
- EventActivation* eventActivation = activation->add_event_activation();
- eventActivation->set_atom_matcher_id(startMatcher.id());
- eventActivation->set_ttl_seconds(5);
-
- unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- EventMetric* metric = config.add_event_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- EventMetric* metric = config.add_event_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- Predicate linkPredicate = CreateScreenIsOffPredicate();
- *config.add_predicate() = linkPredicate;
-
- EventMetric* metric = config.add_event_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
- // Doesn't make sense as a real metric definition, but suffices as a separate predicate
- // From the one in the condition.
- MetricConditionLink* link = metric->add_links();
- link->set_condition(linkPredicate.id());
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- EventMetric* metric = config.add_event_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
-
- MetricActivation* activation = config.add_metric_activation();
- activation->set_metric_id(12345);
- EventActivation* eventActivation = activation->add_event_activation();
- eventActivation->set_atom_matcher_id(startMatcher.id());
- eventActivation->set_ttl_seconds(5);
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestCountMetricPreserve) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
- State sliceState = CreateScreenState();
- *config.add_state() = sliceState;
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
- metric->add_slice_by_state(sliceState.id());
- metric->set_bucket(ONE_HOUR);
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestCountMetricDefinitionChange) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
- metric->set_bucket(ONE_HOUR);
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- // Change bucket size, which should change the proto, causing replacement.
- metric->set_bucket(TEN_MINUTES);
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestCountMetricWhatChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
- metric->set_bucket(ONE_HOUR);
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestCountMetricConditionChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
- metric->set_bucket(ONE_HOUR);
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestCountMetricStateChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- State sliceState = CreateScreenState();
- *config.add_state() = sliceState;
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->add_slice_by_state(sliceState.id());
- metric->set_bucket(ONE_HOUR);
-
- // Create an initial config.
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestGaugeMetricPreserve) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- *config.add_gauge_metric() = createGaugeMetric(
- "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
-
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestGaugeMetricDefinitionChange) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- *config.add_gauge_metric() = createGaugeMetric(
- "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
-
- EXPECT_TRUE(initConfig(config));
-
- // Change split bucket on app upgrade, which should change the proto, causing replacement.
- config.mutable_gauge_metric(0)->set_split_bucket_for_app_upgrade(false);
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestGaugeMetricWhatChanged) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- *config.add_gauge_metric() = createGaugeMetric(
- "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
-
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestGaugeMetricConditionChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- *config.add_gauge_metric() = createGaugeMetric(
- "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
-
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestGaugeMetricTriggerEventChanged) {
- StatsdConfig config;
- AtomMatcher triggerEvent = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = triggerEvent;
- AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- *config.add_gauge_metric() = createGaugeMetric(
- "GAUGE1", whatMatcher.id(), GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerEvent.id());
-
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestDurationMetricPreserve) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate what = CreateScreenIsOnPredicate();
- *config.add_predicate() = what;
- Predicate condition = CreateScreenIsOffPredicate();
- *config.add_predicate() = condition;
-
- State sliceState = CreateScreenState();
- *config.add_state() = sliceState;
-
- *config.add_duration_metric() =
- createDurationMetric("DURATION1", what.id(), condition.id(), {sliceState.id()});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestDurationMetricDefinitionChange) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate what = CreateScreenIsOnPredicate();
- *config.add_predicate() = what;
-
- *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
- EXPECT_TRUE(initConfig(config));
-
- config.mutable_duration_metric(0)->set_aggregation_type(DurationMetric::MAX_SPARSE);
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap, /*replacedMatchers*/ {},
- /*replacedConditions=*/{}, /*replacedStates=*/{},
- metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestDurationMetricWhatChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate what = CreateScreenIsOnPredicate();
- *config.add_predicate() = what;
-
- *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestDurationMetricConditionChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate what = CreateScreenIsOnPredicate();
- *config.add_predicate() = what;
- Predicate condition = CreateScreenIsOffPredicate();
- *config.add_predicate() = condition;
-
- *config.add_duration_metric() = createDurationMetric("DURATION", what.id(), condition.id(), {});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
-
- Predicate what = CreateScreenIsOnPredicate();
- *config.add_predicate() = what;
-
- State sliceState = CreateScreenState();
- *config.add_state() = sliceState;
-
- *config.add_duration_metric() =
- createDurationMetric("DURATION1", what.id(), nullopt, {sliceState.id()});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestValueMetricPreserve) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
- State sliceState = CreateScreenState();
- *config.add_state() = sliceState;
-
- *config.add_value_metric() =
- createValueMetric("VALUE1", whatMatcher, predicate.id(), {sliceState.id()});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
- EXPECT_TRUE(initConfig(config));
-
- // Change skip zero diff output, which should change the proto, causing replacement.
- config.mutable_value_metric(0)->set_skip_zero_diff_output(true);
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
- metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) {
- StatsdConfig config;
- AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = startMatcher;
- AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = stopMatcher;
- AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- Predicate predicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = predicate;
-
- *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, predicate.id(), {});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
- /*replacedStates=*/{}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- State sliceState = CreateScreenState();
- *config.add_state() = sliceState;
-
- *config.add_value_metric() =
- createValueMetric("VALUE1", whatMatcher, nullopt, {sliceState.id()});
- EXPECT_TRUE(initConfig(config));
-
- unordered_map<int64_t, int> metricToActivationMap;
- vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
- EXPECT_TRUE(determineAllMetricUpdateStatuses(
- config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
- /*replacedMatchers*/ {}, /*replacedConditions=*/{},
- /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
- EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
- StatsdConfig config;
-
- // Add atom matchers/predicates. These are mostly needed for initStatsdConfig
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
- int64_t matcher4Id = matcher4.id();
- *config.add_atom_matcher() = matcher4;
-
- AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
- int64_t matcher5Id = matcher5.id();
- *config.add_atom_matcher() = matcher5;
-
- Predicate predicate1 = CreateScreenIsOnPredicate();
- int64_t predicate1Id = predicate1.id();
- *config.add_predicate() = predicate1;
-
- Predicate predicate2 = CreateScheduledJobPredicate();
- int64_t predicate2Id = predicate2.id();
- *config.add_predicate() = predicate2;
-
- // Add a few event metrics.
- // Will be preserved.
- EventMetric event1 = createEventMetric("EVENT1", matcher1Id, predicate2Id);
- int64_t event1Id = event1.id();
- *config.add_event_metric() = event1;
-
- // Will be replaced.
- EventMetric event2 = createEventMetric("EVENT2", matcher2Id, nullopt);
- int64_t event2Id = event2.id();
- *config.add_event_metric() = event2;
-
- // Will be replaced.
- EventMetric event3 = createEventMetric("EVENT3", matcher3Id, nullopt);
- int64_t event3Id = event3.id();
- *config.add_event_metric() = event3;
-
- MetricActivation event3Activation;
- event3Activation.set_metric_id(event3Id);
- EventActivation* eventActivation = event3Activation.add_event_activation();
- eventActivation->set_atom_matcher_id(matcher5Id);
- eventActivation->set_ttl_seconds(5);
- *config.add_metric_activation() = event3Activation;
-
- // Will be replaced.
- EventMetric event4 = createEventMetric("EVENT4", matcher4Id, predicate1Id);
- int64_t event4Id = event4.id();
- *config.add_event_metric() = event4;
-
- // Will be deleted.
- EventMetric event5 = createEventMetric("EVENT5", matcher5Id, nullopt);
- int64_t event5Id = event5.id();
- *config.add_event_metric() = event5;
-
- EXPECT_TRUE(initConfig(config));
-
- // Used later to ensure the condition wizard is replaced. Get it before doing the update.
- sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
- EXPECT_EQ(oldConditionWizard->getStrongCount(), oldMetricProducers.size() + 1);
-
- // Add a condition to event2, causing it to be replaced.
- event2.set_condition(predicate1Id);
-
- // Mark matcher 5 as replaced. Causes event3 to be replaced.
- set<int64_t> replacedMatchers;
- replacedMatchers.insert(matcher5Id);
-
- // Mark predicate 1 as replaced. Causes event4 to be replaced.
- set<int64_t> replacedConditions;
- replacedConditions.insert(predicate1Id);
-
- // Fake that predicate 2 is true.
- ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
- oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
- EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
-
- // New event metric. Should have an initial condition of true since it depends on predicate2.
- EventMetric event6 = createEventMetric("EVENT6", matcher3Id, predicate2Id);
- int64_t event6Id = event6.id();
- MetricActivation event6Activation;
- event6Activation.set_metric_id(event6Id);
- eventActivation = event6Activation.add_event_activation();
- eventActivation->set_atom_matcher_id(matcher5Id);
- eventActivation->set_ttl_seconds(20);
-
- // Map the matchers and predicates in reverse order to force the indices to change.
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher5Index = 0;
- newAtomMatchingTrackerMap[matcher5Id] = 0;
- const int matcher4Index = 1;
- newAtomMatchingTrackerMap[matcher4Id] = 1;
- const int matcher3Index = 2;
- newAtomMatchingTrackerMap[matcher3Id] = 2;
- const int matcher2Index = 3;
- newAtomMatchingTrackerMap[matcher2Id] = 3;
- const int matcher1Index = 4;
- newAtomMatchingTrackerMap[matcher1Id] = 4;
- // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
- std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
- newAtomMatchingTrackers.begin());
-
- std::unordered_map<int64_t, int> newConditionTrackerMap;
- const int predicate2Index = 0;
- newConditionTrackerMap[predicate2Id] = 0;
- const int predicate1Index = 1;
- newConditionTrackerMap[predicate1Id] = 1;
- // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<ConditionTracker>> newConditionTrackers(2);
- std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
- newConditionTrackers.begin());
- // Fake that predicate2 is true.
- vector<ConditionState> conditionCache = {ConditionState::kTrue, ConditionState::kUnknown};
-
- StatsdConfig newConfig;
- *newConfig.add_event_metric() = event6;
- const int event6Index = 0;
- *newConfig.add_event_metric() = event3;
- const int event3Index = 1;
- *newConfig.add_event_metric() = event1;
- const int event1Index = 2;
- *newConfig.add_event_metric() = event4;
- const int event4Index = 3;
- *newConfig.add_event_metric() = event2;
- const int event2Index = 4;
- *newConfig.add_metric_activation() = event3Activation;
- *newConfig.add_metric_activation() = event6Activation;
-
- // Output data structures to validate.
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
- newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
- newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
- /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- unordered_map<int64_t, int> expectedMetricProducerMap = {
- {event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index},
- {event4Id, event4Index}, {event6Id, event6Index},
- };
- EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
- EXPECT_EQ(replacedMetrics, set<int64_t>({event2Id, event3Id, event4Id}));
-
- // Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 5);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)],
- newMetricProducers[newMetricProducerMap.at(event1Id)]);
-
- // Make sure replaced metrics are different.
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)],
- newMetricProducers[newMetricProducerMap.at(event2Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)],
- newMetricProducers[newMetricProducerMap.at(event3Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event4Id)],
- newMetricProducers[newMetricProducerMap.at(event4Id)]);
-
- // Verify the conditionToMetricMap.
- ASSERT_EQ(conditionToMetricMap.size(), 2);
- const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
- EXPECT_THAT(condition1Metrics, UnorderedElementsAre(event2Index, event4Index));
- const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
- EXPECT_THAT(condition2Metrics, UnorderedElementsAre(event1Index, event6Index));
-
- // Verify the trackerToMetricMap.
- ASSERT_EQ(trackerToMetricMap.size(), 4);
- const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
- EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(event1Index));
- const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
- EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(event2Index));
- const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
- EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(event3Index, event6Index));
- const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
- EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(event4Index));
-
- // Verify event activation/deactivation maps.
- ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 1);
- EXPECT_THAT(activationAtomTrackerToMetricMap[matcher5Index],
- UnorderedElementsAre(event3Index, event6Index));
- ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(metricsWithActivation.size(), 2);
- EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(event3Index, event6Index));
-
- // Verify tracker indices/ids/conditions are correct.
- EXPECT_EQ(newMetricProducers[event1Index]->getMetricId(), event1Id);
- EXPECT_EQ(newMetricProducers[event1Index]->mConditionTrackerIndex, predicate2Index);
- EXPECT_EQ(newMetricProducers[event1Index]->mCondition, ConditionState::kTrue);
- EXPECT_EQ(newMetricProducers[event2Index]->getMetricId(), event2Id);
- EXPECT_EQ(newMetricProducers[event2Index]->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(newMetricProducers[event2Index]->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(newMetricProducers[event3Index]->getMetricId(), event3Id);
- EXPECT_EQ(newMetricProducers[event3Index]->mConditionTrackerIndex, -1);
- EXPECT_EQ(newMetricProducers[event3Index]->mCondition, ConditionState::kTrue);
- EXPECT_EQ(newMetricProducers[event4Index]->getMetricId(), event4Id);
- EXPECT_EQ(newMetricProducers[event4Index]->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(newMetricProducers[event4Index]->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(newMetricProducers[event6Index]->getMetricId(), event6Id);
- EXPECT_EQ(newMetricProducers[event6Index]->mConditionTrackerIndex, predicate2Index);
- EXPECT_EQ(newMetricProducers[event6Index]->mCondition, ConditionState::kTrue);
-
- sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
- EXPECT_NE(newConditionWizard, oldConditionWizard);
- EXPECT_EQ(newConditionWizard->getStrongCount(), newMetricProducers.size() + 1);
- oldMetricProducers.clear();
- // Only reference to the old wizard should be the one in the test.
- EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) {
- StatsdConfig config;
-
- // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
- int64_t matcher4Id = matcher4.id();
- *config.add_atom_matcher() = matcher4;
-
- AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
- int64_t matcher5Id = matcher5.id();
- *config.add_atom_matcher() = matcher5;
-
- Predicate predicate1 = CreateScreenIsOnPredicate();
- int64_t predicate1Id = predicate1.id();
- *config.add_predicate() = predicate1;
-
- State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
- int64_t state1Id = state1.id();
- *config.add_state() = state1;
-
- State state2 = CreateScreenState();
- int64_t state2Id = state2.id();
- *config.add_state() = state2;
-
- // Add a few count metrics.
- // Will be preserved.
- CountMetric count1 = createCountMetric("COUNT1", matcher1Id, predicate1Id, {state1Id});
- int64_t count1Id = count1.id();
- *config.add_count_metric() = count1;
-
- // Will be replaced.
- CountMetric count2 = createCountMetric("COUNT2", matcher2Id, nullopt, {});
- int64_t count2Id = count2.id();
- *config.add_count_metric() = count2;
-
- // Will be replaced.
- CountMetric count3 = createCountMetric("COUNT3", matcher3Id, nullopt, {});
- int64_t count3Id = count3.id();
- *config.add_count_metric() = count3;
-
- // Will be replaced.
- CountMetric count4 = createCountMetric("COUNT4", matcher4Id, nullopt, {state2Id});
- int64_t count4Id = count4.id();
- *config.add_count_metric() = count4;
-
- // Will be deleted.
- CountMetric count5 = createCountMetric("COUNT5", matcher5Id, nullopt, {});
- int64_t count5Id = count5.id();
- *config.add_count_metric() = count5;
-
- EXPECT_TRUE(initConfig(config));
-
- // Change bucket size of count2, causing it to be replaced.
- count2.set_bucket(ONE_HOUR);
-
- // Mark matcher 3 as replaced. Causes count3 to be replaced.
- set<int64_t> replacedMatchers;
- replacedMatchers.insert(matcher3Id);
-
- // Mark state 2 as replaced and change the state to be about a different atom.
- // Causes count4 to be replaced.
- set<int64_t> replacedStates;
- replacedStates.insert(state2Id);
- state2.set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED);
-
- // Fake that predicate 1 is true for count metric 1.
- ASSERT_EQ(oldMetricProducers[0]->getMetricId(), count1Id);
- oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
- EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
-
- EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 1);
- // Tell the StateManager that the screen is on.
- unique_ptr<LogEvent> event =
- CreateScreenStateChangedEvent(0, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- StateManager::getInstance().onLogEvent(*event);
-
- // New count metric. Should have an initial condition of true since it depends on predicate1.
- CountMetric count6 = createCountMetric("EVENT6", matcher2Id, predicate1Id, {state1Id});
- int64_t count6Id = count6.id();
-
- // Map the matchers and predicates in reverse order to force the indices to change.
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher5Index = 0;
- newAtomMatchingTrackerMap[matcher5Id] = 0;
- const int matcher4Index = 1;
- newAtomMatchingTrackerMap[matcher4Id] = 1;
- const int matcher3Index = 2;
- newAtomMatchingTrackerMap[matcher3Id] = 2;
- const int matcher2Index = 3;
- newAtomMatchingTrackerMap[matcher2Id] = 3;
- const int matcher1Index = 4;
- newAtomMatchingTrackerMap[matcher1Id] = 4;
- // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
- std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
- newAtomMatchingTrackers.begin());
-
- std::unordered_map<int64_t, int> newConditionTrackerMap;
- const int predicate1Index = 0;
- newConditionTrackerMap[predicate1Id] = 0;
- // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<ConditionTracker>> newConditionTrackers(1);
- std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
- newConditionTrackers.begin());
- // Fake that predicate1 is true for all new metrics.
- vector<ConditionState> conditionCache = {ConditionState::kTrue};
-
- StatsdConfig newConfig;
- *newConfig.add_count_metric() = count6;
- const int count6Index = 0;
- *newConfig.add_count_metric() = count3;
- const int count3Index = 1;
- *newConfig.add_count_metric() = count1;
- const int count1Index = 2;
- *newConfig.add_count_metric() = count4;
- const int count4Index = 3;
- *newConfig.add_count_metric() = count2;
- const int count2Index = 4;
-
- *newConfig.add_state() = state1;
- *newConfig.add_state() = state2;
-
- unordered_map<int64_t, int> stateAtomIdMap;
- unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
- map<int64_t, uint64_t> stateProtoHashes;
- EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
- EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED);
-
- // Output data structures to validate.
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
- newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
- newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
- oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
- conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- unordered_map<int64_t, int> expectedMetricProducerMap = {
- {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index},
- {count4Id, count4Index}, {count6Id, count6Index},
- };
- EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
- EXPECT_EQ(replacedMetrics, set<int64_t>({count2Id, count3Id, count4Id}));
-
- // Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 5);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(count1Id)],
- newMetricProducers[newMetricProducerMap.at(count1Id)]);
-
- // Make sure replaced metrics are different.
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count2Id)],
- newMetricProducers[newMetricProducerMap.at(count2Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count3Id)],
- newMetricProducers[newMetricProducerMap.at(count3Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count4Id)],
- newMetricProducers[newMetricProducerMap.at(count4Id)]);
-
- // Verify the conditionToMetricMap.
- ASSERT_EQ(conditionToMetricMap.size(), 1);
- const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
- EXPECT_THAT(condition1Metrics, UnorderedElementsAre(count1Index, count6Index));
-
- // Verify the trackerToMetricMap.
- ASSERT_EQ(trackerToMetricMap.size(), 4);
- const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
- EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(count1Index));
- const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
- EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(count2Index, count6Index));
- const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
- EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(count3Index));
- const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
- EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(count4Index));
-
- // Verify event activation/deactivation maps.
- ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(metricsWithActivation.size(), 0);
-
- // Verify tracker indices/ids/conditions/states are correct.
- EXPECT_EQ(newMetricProducers[count1Index]->getMetricId(), count1Id);
- EXPECT_EQ(newMetricProducers[count1Index]->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(newMetricProducers[count1Index]->mCondition, ConditionState::kTrue);
- EXPECT_THAT(newMetricProducers[count1Index]->getSlicedStateAtoms(),
- UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
- EXPECT_EQ(newMetricProducers[count2Index]->getMetricId(), count2Id);
- EXPECT_EQ(newMetricProducers[count2Index]->mConditionTrackerIndex, -1);
- EXPECT_EQ(newMetricProducers[count2Index]->mCondition, ConditionState::kTrue);
- EXPECT_TRUE(newMetricProducers[count2Index]->getSlicedStateAtoms().empty());
- EXPECT_EQ(newMetricProducers[count3Index]->getMetricId(), count3Id);
- EXPECT_EQ(newMetricProducers[count3Index]->mConditionTrackerIndex, -1);
- EXPECT_EQ(newMetricProducers[count3Index]->mCondition, ConditionState::kTrue);
- EXPECT_TRUE(newMetricProducers[count3Index]->getSlicedStateAtoms().empty());
- EXPECT_EQ(newMetricProducers[count4Index]->getMetricId(), count4Id);
- EXPECT_EQ(newMetricProducers[count4Index]->mConditionTrackerIndex, -1);
- EXPECT_EQ(newMetricProducers[count4Index]->mCondition, ConditionState::kTrue);
- EXPECT_THAT(newMetricProducers[count4Index]->getSlicedStateAtoms(),
- UnorderedElementsAre(util::BATTERY_SAVER_MODE_STATE_CHANGED));
- EXPECT_EQ(newMetricProducers[count6Index]->getMetricId(), count6Id);
- EXPECT_EQ(newMetricProducers[count6Index]->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(newMetricProducers[count6Index]->mCondition, ConditionState::kTrue);
- EXPECT_THAT(newMetricProducers[count6Index]->getSlicedStateAtoms(),
- UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
-
- oldMetricProducers.clear();
- // Ensure that the screen state StateTracker did not get deleted and replaced.
- EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 2);
- FieldValue screenState;
- StateManager::getInstance().getStateValue(util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY,
- &screenState);
- EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) {
- StatsdConfig config;
-
- // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
- int64_t matcher4Id = matcher4.id();
- *config.add_atom_matcher() = matcher4;
-
- AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
- int64_t matcher5Id = matcher5.id();
- *config.add_atom_matcher() = matcher5;
-
- Predicate predicate1 = CreateScreenIsOnPredicate();
- int64_t predicate1Id = predicate1.id();
- *config.add_predicate() = predicate1;
-
- // Add a few gauge metrics.
- // Will be preserved.
- GaugeMetric gauge1 = createGaugeMetric("GAUGE1", matcher4Id, GaugeMetric::FIRST_N_SAMPLES,
- predicate1Id, matcher1Id);
- int64_t gauge1Id = gauge1.id();
- *config.add_gauge_metric() = gauge1;
-
- // Will be replaced.
- GaugeMetric gauge2 =
- createGaugeMetric("GAUGE2", matcher1Id, GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
- int64_t gauge2Id = gauge2.id();
- *config.add_gauge_metric() = gauge2;
-
- // Will be replaced.
- GaugeMetric gauge3 = createGaugeMetric("GAUGE3", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
- nullopt, matcher3Id);
- int64_t gauge3Id = gauge3.id();
- *config.add_gauge_metric() = gauge3;
-
- // Will be replaced.
- GaugeMetric gauge4 = createGaugeMetric("GAUGE4", matcher3Id, GaugeMetric::RANDOM_ONE_SAMPLE,
- predicate1Id, nullopt);
- int64_t gauge4Id = gauge4.id();
- *config.add_gauge_metric() = gauge4;
-
- // Will be deleted.
- GaugeMetric gauge5 =
- createGaugeMetric("GAUGE5", matcher2Id, GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, {});
- int64_t gauge5Id = gauge5.id();
- *config.add_gauge_metric() = gauge5;
-
- EXPECT_TRUE(initConfig(config));
-
- // Used later to ensure the condition wizard is replaced. Get it before doing the update.
- sp<EventMatcherWizard> oldMatcherWizard =
- static_cast<GaugeMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
- EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
-
- // Change gauge2, causing it to be replaced.
- gauge2.set_max_num_gauge_atoms_per_bucket(50);
-
- // Mark matcher 3 as replaced. Causes gauge3 and gauge4 to be replaced.
- set<int64_t> replacedMatchers = {matcher3Id};
-
- // New gauge metric.
- GaugeMetric gauge6 = createGaugeMetric("GAUGE6", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
- predicate1Id, matcher3Id);
- int64_t gauge6Id = gauge6.id();
-
- // Map the matchers and predicates in reverse order to force the indices to change.
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher5Index = 0;
- newAtomMatchingTrackerMap[matcher5Id] = 0;
- const int matcher4Index = 1;
- newAtomMatchingTrackerMap[matcher4Id] = 1;
- const int matcher3Index = 2;
- newAtomMatchingTrackerMap[matcher3Id] = 2;
- const int matcher2Index = 3;
- newAtomMatchingTrackerMap[matcher2Id] = 3;
- const int matcher1Index = 4;
- newAtomMatchingTrackerMap[matcher1Id] = 4;
- // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
- std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
- newAtomMatchingTrackers.begin());
-
- std::unordered_map<int64_t, int> newConditionTrackerMap;
- const int predicate1Index = 0;
- newConditionTrackerMap[predicate1Id] = 0;
- // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<ConditionTracker>> newConditionTrackers(1);
- std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
- newConditionTrackers.begin());
- // Say that predicate1 is unknown since the initial condition never changed.
- vector<ConditionState> conditionCache = {ConditionState::kUnknown};
-
- StatsdConfig newConfig;
- *newConfig.add_gauge_metric() = gauge6;
- const int gauge6Index = 0;
- *newConfig.add_gauge_metric() = gauge3;
- const int gauge3Index = 1;
- *newConfig.add_gauge_metric() = gauge1;
- const int gauge1Index = 2;
- *newConfig.add_gauge_metric() = gauge4;
- const int gauge4Index = 3;
- *newConfig.add_gauge_metric() = gauge2;
- const int gauge2Index = 4;
-
- // Output data structures to validate.
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
- newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
- newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
- /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- unordered_map<int64_t, int> expectedMetricProducerMap = {
- {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index},
- {gauge4Id, gauge4Index}, {gauge6Id, gauge6Index},
- };
- EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
- EXPECT_EQ(replacedMetrics, set<int64_t>({gauge2Id, gauge3Id, gauge4Id}));
-
- // Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 5);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(gauge1Id)],
- newMetricProducers[newMetricProducerMap.at(gauge1Id)]);
-
- // Make sure replaced metrics are different.
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge2Id)],
- newMetricProducers[newMetricProducerMap.at(gauge2Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge3Id)],
- newMetricProducers[newMetricProducerMap.at(gauge3Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge4Id)],
- newMetricProducers[newMetricProducerMap.at(gauge4Id)]);
-
- // Verify the conditionToMetricMap.
- ASSERT_EQ(conditionToMetricMap.size(), 1);
- const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
- EXPECT_THAT(condition1Metrics, UnorderedElementsAre(gauge1Index, gauge4Index, gauge6Index));
-
- // Verify the trackerToMetricMap.
- ASSERT_EQ(trackerToMetricMap.size(), 4);
- const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
- EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(gauge1Index, gauge2Index));
- const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
- EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gauge3Index, gauge4Index, gauge6Index));
- const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
- EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(gauge1Index));
- const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
- EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(gauge3Index, gauge6Index));
-
- // Verify event activation/deactivation maps.
- ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(metricsWithActivation.size(), 0);
-
- // Verify tracker indices/ids/conditions/states are correct.
- GaugeMetricProducer* gaugeProducer1 =
- static_cast<GaugeMetricProducer*>(newMetricProducers[gauge1Index].get());
- EXPECT_EQ(gaugeProducer1->getMetricId(), gauge1Id);
- EXPECT_EQ(gaugeProducer1->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(gaugeProducer1->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(gaugeProducer1->mWhatMatcherIndex, matcher4Index);
- GaugeMetricProducer* gaugeProducer2 =
- static_cast<GaugeMetricProducer*>(newMetricProducers[gauge2Index].get());
- EXPECT_EQ(gaugeProducer2->getMetricId(), gauge2Id);
- EXPECT_EQ(gaugeProducer2->mConditionTrackerIndex, -1);
- EXPECT_EQ(gaugeProducer2->mCondition, ConditionState::kTrue);
- EXPECT_EQ(gaugeProducer2->mWhatMatcherIndex, matcher1Index);
- GaugeMetricProducer* gaugeProducer3 =
- static_cast<GaugeMetricProducer*>(newMetricProducers[gauge3Index].get());
- EXPECT_EQ(gaugeProducer3->getMetricId(), gauge3Id);
- EXPECT_EQ(gaugeProducer3->mConditionTrackerIndex, -1);
- EXPECT_EQ(gaugeProducer3->mCondition, ConditionState::kTrue);
- EXPECT_EQ(gaugeProducer3->mWhatMatcherIndex, matcher5Index);
- GaugeMetricProducer* gaugeProducer4 =
- static_cast<GaugeMetricProducer*>(newMetricProducers[gauge4Index].get());
- EXPECT_EQ(gaugeProducer4->getMetricId(), gauge4Id);
- EXPECT_EQ(gaugeProducer4->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(gaugeProducer4->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(gaugeProducer4->mWhatMatcherIndex, matcher3Index);
- GaugeMetricProducer* gaugeProducer6 =
- static_cast<GaugeMetricProducer*>(newMetricProducers[gauge6Index].get());
- EXPECT_EQ(gaugeProducer6->getMetricId(), gauge6Id);
- EXPECT_EQ(gaugeProducer6->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(gaugeProducer6->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(gaugeProducer6->mWhatMatcherIndex, matcher5Index);
-
- sp<EventMatcherWizard> newMatcherWizard = gaugeProducer1->mEventMatcherWizard;
- EXPECT_NE(newMatcherWizard, oldMatcherWizard);
- EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
- oldMetricProducers.clear();
- // Only reference to the old wizard should be the one in the test.
- EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) {
- StatsdConfig config;
- // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateAcquireWakelockAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- AtomMatcher matcher4 = CreateReleaseWakelockAtomMatcher();
- int64_t matcher4Id = matcher4.id();
- *config.add_atom_matcher() = matcher4;
-
- AtomMatcher matcher5 = CreateMoveToForegroundAtomMatcher();
- int64_t matcher5Id = matcher5.id();
- *config.add_atom_matcher() = matcher5;
-
- AtomMatcher matcher6 = CreateMoveToBackgroundAtomMatcher();
- int64_t matcher6Id = matcher6.id();
- *config.add_atom_matcher() = matcher6;
-
- AtomMatcher matcher7 = CreateBatteryStateNoneMatcher();
- int64_t matcher7Id = matcher7.id();
- *config.add_atom_matcher() = matcher7;
-
- AtomMatcher matcher8 = CreateBatteryStateUsbMatcher();
- int64_t matcher8Id = matcher8.id();
- *config.add_atom_matcher() = matcher8;
-
- Predicate predicate1 = CreateScreenIsOnPredicate();
- int64_t predicate1Id = predicate1.id();
- *config.add_predicate() = predicate1;
-
- Predicate predicate2 = CreateScreenIsOffPredicate();
- int64_t predicate2Id = predicate2.id();
- *config.add_predicate() = predicate2;
-
- Predicate predicate3 = CreateDeviceUnpluggedPredicate();
- int64_t predicate3Id = predicate3.id();
- *config.add_predicate() = predicate3;
-
- Predicate predicate4 = CreateIsInBackgroundPredicate();
- *predicate4.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1});
- int64_t predicate4Id = predicate4.id();
- *config.add_predicate() = predicate4;
-
- Predicate predicate5 = CreateHoldingWakelockPredicate();
- *predicate5.mutable_simple_predicate()->mutable_dimensions() =
- CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- predicate5.mutable_simple_predicate()->set_stop_all(matcher7Id);
- int64_t predicate5Id = predicate5.id();
- *config.add_predicate() = predicate5;
-
- State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
- int64_t state1Id = state1.id();
- *config.add_state() = state1;
-
- State state2 = CreateScreenState();
- int64_t state2Id = state2.id();
- *config.add_state() = state2;
-
- // Add a few duration metrics.
- // Will be preserved.
- DurationMetric duration1 =
- createDurationMetric("DURATION1", predicate5Id, predicate4Id, {state2Id});
- *duration1.mutable_dimensions_in_what() =
- CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- MetricConditionLink* link = duration1.add_links();
- link->set_condition(predicate4Id);
- *link->mutable_fields_in_what() =
- CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- *link->mutable_fields_in_condition() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
- int64_t duration1Id = duration1.id();
- *config.add_duration_metric() = duration1;
-
- // Will be replaced.
- DurationMetric duration2 = createDurationMetric("DURATION2", predicate1Id, nullopt, {});
- int64_t duration2Id = duration2.id();
- *config.add_duration_metric() = duration2;
-
- // Will be replaced.
- DurationMetric duration3 = createDurationMetric("DURATION3", predicate3Id, nullopt, {state1Id});
- int64_t duration3Id = duration3.id();
- *config.add_duration_metric() = duration3;
-
- // Will be replaced.
- DurationMetric duration4 = createDurationMetric("DURATION4", predicate3Id, predicate2Id, {});
- int64_t duration4Id = duration4.id();
- *config.add_duration_metric() = duration4;
-
- // Will be deleted.
- DurationMetric duration5 = createDurationMetric("DURATION5", predicate2Id, nullopt, {});
- int64_t duration5Id = duration5.id();
- *config.add_duration_metric() = duration5;
-
- EXPECT_TRUE(initConfig(config));
-
- // Make some sliced conditions true.
- int uid1 = 10;
- int uid2 = 11;
- vector<MatchingState> matchingStates(8, MatchingState::kNotMatched);
- matchingStates[2] = kMatched;
- vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
- vector<bool> changedCache(5, false);
- unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1");
- oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
- conditionCache, changedCache);
- EXPECT_TRUE(oldConditionTrackers[4]->isSliced());
- EXPECT_TRUE(changedCache[4]);
- EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
- oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
-
- fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
- fill(changedCache.begin(), changedCache.end(), false);
- event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid2}, {"tag"}, "wl2");
- oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
- conditionCache, changedCache);
- EXPECT_TRUE(changedCache[4]);
- EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
- oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
-
- // Used later to ensure the condition wizard is replaced. Get it before doing the update.
- // The duration trackers have a pointer to the wizard, and 2 trackers were created above.
- sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
- EXPECT_EQ(oldConditionWizard->getStrongCount(), 8);
-
- // Replace predicate1, predicate3, and state1. Causes duration2/3/4 to be replaced.
- set<int64_t> replacedConditions({predicate1Id, predicate2Id});
- set<int64_t> replacedStates({state1Id});
-
- // New duration metric.
- DurationMetric duration6 = createDurationMetric("DURATION6", predicate4Id, predicate5Id, {});
- *duration6.mutable_dimensions_in_what() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
- link = duration6.add_links();
- link->set_condition(predicate5Id);
- *link->mutable_fields_in_what() =
- CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
- *link->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- int64_t duration6Id = duration6.id();
-
- // Map the matchers and predicates in reverse order to force the indices to change.
- const int matcher8Index = 0, matcher7Index = 1, matcher6Index = 2, matcher5Index = 3,
- matcher4Index = 4, matcher3Index = 5, matcher2Index = 6, matcher1Index = 7;
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap({{matcher8Id, matcher8Index},
- {matcher7Id, matcher7Index},
- {matcher6Id, matcher6Index},
- {matcher5Id, matcher5Index},
- {matcher4Id, matcher4Index},
- {matcher3Id, matcher3Index},
- {matcher2Id, matcher2Index},
- {matcher1Id, matcher1Index}});
- // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(8);
- reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
- newAtomMatchingTrackers.begin());
-
- const int predicate5Index = 0, predicate4Index = 1, predicate3Index = 2, predicate2Index = 3,
- predicate1Index = 4;
- std::unordered_map<int64_t, int> newConditionTrackerMap({
- {predicate5Id, predicate5Index},
- {predicate4Id, predicate4Index},
- {predicate3Id, predicate3Index},
- {predicate2Id, predicate2Index},
- {predicate1Id, predicate1Index},
- });
- // Use the existing conditionTrackers and reinitialize them to get the initial condition cache.
- vector<sp<ConditionTracker>> newConditionTrackers(5);
- reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
- newConditionTrackers.begin());
- vector<Predicate> conditionProtos(5);
- reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin());
- for (int i = 0; i < newConditionTrackers.size(); i++) {
- EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated(
- conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap,
- newConditionTrackerMap));
- }
- vector<bool> cycleTracker(5, false);
- fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
- for (int i = 0; i < newConditionTrackers.size(); i++) {
- EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers,
- newConditionTrackerMap, cycleTracker,
- conditionCache));
- }
- // Predicate5 should be true since 2 uids have wakelocks
- EXPECT_EQ(conditionCache, vector({kTrue, kUnknown, kUnknown, kUnknown, kUnknown}));
-
- StatsdConfig newConfig;
- *newConfig.add_duration_metric() = duration6;
- const int duration6Index = 0;
- *newConfig.add_duration_metric() = duration3;
- const int duration3Index = 1;
- *newConfig.add_duration_metric() = duration1;
- const int duration1Index = 2;
- *newConfig.add_duration_metric() = duration4;
- const int duration4Index = 3;
- *newConfig.add_duration_metric() = duration2;
- const int duration2Index = 4;
-
- for (const Predicate& predicate : conditionProtos) {
- *newConfig.add_predicate() = predicate;
- }
- *newConfig.add_state() = state1;
- *newConfig.add_state() = state2;
- unordered_map<int64_t, int> stateAtomIdMap;
- unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
- map<int64_t, uint64_t> stateProtoHashes;
- EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
-
- // Output data structures to validate.
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
- newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
- newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
- oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
- conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- unordered_map<int64_t, int> expectedMetricProducerMap = {
- {duration1Id, duration1Index}, {duration2Id, duration2Index},
- {duration3Id, duration3Index}, {duration4Id, duration4Index},
- {duration6Id, duration6Index},
- };
- EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
- EXPECT_EQ(replacedMetrics, set<int64_t>({duration2Id, duration3Id, duration4Id}));
- // Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 5);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)],
- newMetricProducers[newMetricProducerMap.at(duration1Id)]);
-
- // Make sure replaced metrics are different.
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration2Id)],
- newMetricProducers[newMetricProducerMap.at(duration2Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration3Id)],
- newMetricProducers[newMetricProducerMap.at(duration3Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration4Id)],
- newMetricProducers[newMetricProducerMap.at(duration4Id)]);
-
- // Verify the conditionToMetricMap. Note that the "what" is not in this map.
- ASSERT_EQ(conditionToMetricMap.size(), 3);
- const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
- EXPECT_THAT(condition2Metrics, UnorderedElementsAre(duration4Index));
- const vector<int>& condition4Metrics = conditionToMetricMap[predicate4Index];
- EXPECT_THAT(condition4Metrics, UnorderedElementsAre(duration1Index));
- const vector<int>& condition5Metrics = conditionToMetricMap[predicate5Index];
- EXPECT_THAT(condition5Metrics, UnorderedElementsAre(duration6Index));
-
- // Verify the trackerToMetricMap. The start/stop/stopall indices from the "what" should be here.
- ASSERT_EQ(trackerToMetricMap.size(), 8);
- const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
- EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(duration2Index));
- const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
- EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(duration2Index));
- const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
- EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(duration1Index));
- const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
- EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(duration1Index));
- const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
- EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(duration6Index));
- const vector<int>& matcher6Metrics = trackerToMetricMap[matcher6Index];
- EXPECT_THAT(matcher6Metrics, UnorderedElementsAre(duration6Index));
- const vector<int>& matcher7Metrics = trackerToMetricMap[matcher7Index];
- EXPECT_THAT(matcher7Metrics,
- UnorderedElementsAre(duration1Index, duration3Index, duration4Index));
- const vector<int>& matcher8Metrics = trackerToMetricMap[matcher8Index];
- EXPECT_THAT(matcher8Metrics, UnorderedElementsAre(duration3Index, duration4Index));
-
- // Verify event activation/deactivation maps.
- ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(metricsWithActivation.size(), 0);
-
- // Verify tracker indices/ids/conditions are correct.
- DurationMetricProducer* durationProducer1 =
- static_cast<DurationMetricProducer*>(newMetricProducers[duration1Index].get());
- EXPECT_EQ(durationProducer1->getMetricId(), duration1Id);
- EXPECT_EQ(durationProducer1->mConditionTrackerIndex, predicate4Index);
- EXPECT_EQ(durationProducer1->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(durationProducer1->mStartIndex, matcher3Index);
- EXPECT_EQ(durationProducer1->mStopIndex, matcher4Index);
- EXPECT_EQ(durationProducer1->mStopAllIndex, matcher7Index);
- EXPECT_EQ(durationProducer1->mCurrentSlicedDurationTrackerMap.size(), 2);
- for (const auto& durationTrackerIt : durationProducer1->mCurrentSlicedDurationTrackerMap) {
- EXPECT_EQ(durationTrackerIt.second->mConditionTrackerIndex, predicate4Index);
- }
- DurationMetricProducer* durationProducer2 =
- static_cast<DurationMetricProducer*>(newMetricProducers[duration2Index].get());
- EXPECT_EQ(durationProducer2->getMetricId(), duration2Id);
- EXPECT_EQ(durationProducer2->mConditionTrackerIndex, -1);
- EXPECT_EQ(durationProducer2->mCondition, ConditionState::kTrue);
- EXPECT_EQ(durationProducer2->mStartIndex, matcher1Index);
- EXPECT_EQ(durationProducer2->mStopIndex, matcher2Index);
- EXPECT_EQ(durationProducer2->mStopAllIndex, -1);
- DurationMetricProducer* durationProducer3 =
- static_cast<DurationMetricProducer*>(newMetricProducers[duration3Index].get());
- EXPECT_EQ(durationProducer3->getMetricId(), duration3Id);
- EXPECT_EQ(durationProducer3->mConditionTrackerIndex, -1);
- EXPECT_EQ(durationProducer3->mCondition, ConditionState::kTrue);
- EXPECT_EQ(durationProducer3->mStartIndex, matcher7Index);
- EXPECT_EQ(durationProducer3->mStopIndex, matcher8Index);
- EXPECT_EQ(durationProducer3->mStopAllIndex, -1);
- DurationMetricProducer* durationProducer4 =
- static_cast<DurationMetricProducer*>(newMetricProducers[duration4Index].get());
- EXPECT_EQ(durationProducer4->getMetricId(), duration4Id);
- EXPECT_EQ(durationProducer4->mConditionTrackerIndex, predicate2Index);
- EXPECT_EQ(durationProducer4->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(durationProducer4->mStartIndex, matcher7Index);
- EXPECT_EQ(durationProducer4->mStopIndex, matcher8Index);
- EXPECT_EQ(durationProducer4->mStopAllIndex, -1);
- DurationMetricProducer* durationProducer6 =
- static_cast<DurationMetricProducer*>(newMetricProducers[duration6Index].get());
- EXPECT_EQ(durationProducer6->getMetricId(), duration6Id);
- EXPECT_EQ(durationProducer6->mConditionTrackerIndex, predicate5Index);
- // TODO(b/167491517): should this be unknown since the condition is sliced?
- EXPECT_EQ(durationProducer6->mCondition, ConditionState::kTrue);
- EXPECT_EQ(durationProducer6->mStartIndex, matcher6Index);
- EXPECT_EQ(durationProducer6->mStopIndex, matcher5Index);
- EXPECT_EQ(durationProducer6->mStopAllIndex, -1);
-
- sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
- EXPECT_NE(newConditionWizard, oldConditionWizard);
- EXPECT_EQ(newConditionWizard->getStrongCount(), 8);
- oldMetricProducers.clear();
- // Only reference to the old wizard should be the one in the test.
- EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateValueMetrics) {
- StatsdConfig config;
-
- // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
- int64_t matcher4Id = matcher4.id();
- *config.add_atom_matcher() = matcher4;
-
- AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
- int64_t matcher5Id = matcher5.id();
- *config.add_atom_matcher() = matcher5;
-
- Predicate predicate1 = CreateScreenIsOnPredicate();
- int64_t predicate1Id = predicate1.id();
- *config.add_predicate() = predicate1;
-
- Predicate predicate2 = CreateScreenIsOffPredicate();
- int64_t predicate2Id = predicate2.id();
- *config.add_predicate() = predicate2;
-
- State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
- int64_t state1Id = state1.id();
- *config.add_state() = state1;
-
- State state2 = CreateScreenState();
- int64_t state2Id = state2.id();
- *config.add_state() = state2;
-
- // Add a few value metrics.
- // Note that these will not work as "real" metrics since the value field is always 2.
- // Will be preserved.
- ValueMetric value1 = createValueMetric("VALUE1", matcher4, predicate1Id, {state1Id});
- int64_t value1Id = value1.id();
- *config.add_value_metric() = value1;
-
- // Will be replaced - definition change.
- ValueMetric value2 = createValueMetric("VALUE2", matcher1, nullopt, {});
- int64_t value2Id = value2.id();
- *config.add_value_metric() = value2;
-
- // Will be replaced - condition change.
- ValueMetric value3 = createValueMetric("VALUE3", matcher5, predicate2Id, {});
- int64_t value3Id = value3.id();
- *config.add_value_metric() = value3;
-
- // Will be replaced - state change.
- ValueMetric value4 = createValueMetric("VALUE4", matcher3, nullopt, {state2Id});
- int64_t value4Id = value4.id();
- *config.add_value_metric() = value4;
-
- // Will be deleted.
- ValueMetric value5 = createValueMetric("VALUE5", matcher2, nullopt, {});
- int64_t value5Id = value5.id();
- *config.add_value_metric() = value5;
-
- EXPECT_TRUE(initConfig(config));
-
- // Used later to ensure the condition wizard is replaced. Get it before doing the update.
- sp<EventMatcherWizard> oldMatcherWizard =
- static_cast<ValueMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
- EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
-
- // Change value2, causing it to be replaced.
- value2.set_aggregation_type(ValueMetric::AVG);
-
- // Mark predicate 2 as replaced. Causes value3 to be replaced.
- set<int64_t> replacedConditions = {predicate2Id};
-
- // Mark state 2 as replaced. Causes value4 to be replaced.
- set<int64_t> replacedStates = {state2Id};
-
- // New value metric.
- ValueMetric value6 = createValueMetric("VALUE6", matcher5, predicate1Id, {state1Id});
- int64_t value6Id = value6.id();
-
- // Map the matchers and predicates in reverse order to force the indices to change.
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher5Index = 0;
- newAtomMatchingTrackerMap[matcher5Id] = 0;
- const int matcher4Index = 1;
- newAtomMatchingTrackerMap[matcher4Id] = 1;
- const int matcher3Index = 2;
- newAtomMatchingTrackerMap[matcher3Id] = 2;
- const int matcher2Index = 3;
- newAtomMatchingTrackerMap[matcher2Id] = 3;
- const int matcher1Index = 4;
- newAtomMatchingTrackerMap[matcher1Id] = 4;
- // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
- std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
- newAtomMatchingTrackers.begin());
-
- std::unordered_map<int64_t, int> newConditionTrackerMap;
- const int predicate2Index = 0;
- newConditionTrackerMap[predicate2Id] = 0;
- const int predicate1Index = 1;
- newConditionTrackerMap[predicate1Id] = 1;
- // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<ConditionTracker>> newConditionTrackers(2);
- std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
- newConditionTrackers.begin());
- // Say that predicate1 & predicate2 is unknown since the initial condition never changed.
- vector<ConditionState> conditionCache = {ConditionState::kUnknown, ConditionState::kUnknown};
-
- StatsdConfig newConfig;
- *newConfig.add_value_metric() = value6;
- const int value6Index = 0;
- *newConfig.add_value_metric() = value3;
- const int value3Index = 1;
- *newConfig.add_value_metric() = value1;
- const int value1Index = 2;
- *newConfig.add_value_metric() = value4;
- const int value4Index = 3;
- *newConfig.add_value_metric() = value2;
- const int value2Index = 4;
-
- *newConfig.add_state() = state1;
- *newConfig.add_state() = state2;
-
- unordered_map<int64_t, int> stateAtomIdMap;
- unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
- map<int64_t, uint64_t> stateProtoHashes;
- EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
-
- // Output data structures to validate.
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
- newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
- newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
- oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
- conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- unordered_map<int64_t, int> expectedMetricProducerMap = {
- {value1Id, value1Index}, {value2Id, value2Index}, {value3Id, value3Index},
- {value4Id, value4Index}, {value6Id, value6Index},
- };
- EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
- EXPECT_EQ(replacedMetrics, set<int64_t>({value2Id, value3Id, value4Id}));
-
- // Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 5);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(value1Id)],
- newMetricProducers[newMetricProducerMap.at(value1Id)]);
-
- // Make sure replaced metrics are different.
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value2Id)],
- newMetricProducers[newMetricProducerMap.at(value2Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value3Id)],
- newMetricProducers[newMetricProducerMap.at(value3Id)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value4Id)],
- newMetricProducers[newMetricProducerMap.at(value4Id)]);
-
- // Verify the conditionToMetricMap.
- ASSERT_EQ(conditionToMetricMap.size(), 2);
- const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
- EXPECT_THAT(condition1Metrics, UnorderedElementsAre(value1Index, value6Index));
- const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
- EXPECT_THAT(condition2Metrics, UnorderedElementsAre(value3Index));
-
- // Verify the trackerToMetricMap.
- ASSERT_EQ(trackerToMetricMap.size(), 4);
- const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
- EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(value2Index));
- const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
- EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(value4Index));
- const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
- EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(value1Index));
- const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
- EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(value3Index, value6Index));
-
- // Verify event activation/deactivation maps.
- ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(metricsWithActivation.size(), 0);
-
- // Verify tracker indices/ids/conditions/states are correct.
- ValueMetricProducer* valueProducer1 =
- static_cast<ValueMetricProducer*>(newMetricProducers[value1Index].get());
- EXPECT_EQ(valueProducer1->getMetricId(), value1Id);
- EXPECT_EQ(valueProducer1->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(valueProducer1->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(valueProducer1->mWhatMatcherIndex, matcher4Index);
- ValueMetricProducer* valueProducer2 =
- static_cast<ValueMetricProducer*>(newMetricProducers[value2Index].get());
- EXPECT_EQ(valueProducer2->getMetricId(), value2Id);
- EXPECT_EQ(valueProducer2->mConditionTrackerIndex, -1);
- EXPECT_EQ(valueProducer2->mCondition, ConditionState::kTrue);
- EXPECT_EQ(valueProducer2->mWhatMatcherIndex, matcher1Index);
- ValueMetricProducer* valueProducer3 =
- static_cast<ValueMetricProducer*>(newMetricProducers[value3Index].get());
- EXPECT_EQ(valueProducer3->getMetricId(), value3Id);
- EXPECT_EQ(valueProducer3->mConditionTrackerIndex, predicate2Index);
- EXPECT_EQ(valueProducer3->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(valueProducer3->mWhatMatcherIndex, matcher5Index);
- ValueMetricProducer* valueProducer4 =
- static_cast<ValueMetricProducer*>(newMetricProducers[value4Index].get());
- EXPECT_EQ(valueProducer4->getMetricId(), value4Id);
- EXPECT_EQ(valueProducer4->mConditionTrackerIndex, -1);
- EXPECT_EQ(valueProducer4->mCondition, ConditionState::kTrue);
- EXPECT_EQ(valueProducer4->mWhatMatcherIndex, matcher3Index);
- ValueMetricProducer* valueProducer6 =
- static_cast<ValueMetricProducer*>(newMetricProducers[value6Index].get());
- EXPECT_EQ(valueProducer6->getMetricId(), value6Id);
- EXPECT_EQ(valueProducer6->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(valueProducer6->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(valueProducer6->mWhatMatcherIndex, matcher5Index);
-
- sp<EventMatcherWizard> newMatcherWizard = valueProducer1->mEventMatcherWizard;
- EXPECT_NE(newMatcherWizard, oldMatcherWizard);
- EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
- oldMetricProducers.clear();
- // Only reference to the old wizard should be the one in the test.
- EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
- StatsdConfig config;
- // Add atom matchers
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
- int64_t matcher4Id = matcher4.id();
- *config.add_atom_matcher() = matcher4;
-
- // Add an event metric with multiple activations.
- EventMetric event1 = createEventMetric("EVENT1", matcher1Id, nullopt);
- int64_t event1Id = event1.id();
- *config.add_event_metric() = event1;
-
- int64_t matcher2TtlSec = 2, matcher3TtlSec = 3, matcher4TtlSec = 4;
- MetricActivation metricActivation;
- metricActivation.set_metric_id(event1Id);
- EventActivation* activation = metricActivation.add_event_activation();
- activation->set_atom_matcher_id(matcher2Id);
- activation->set_ttl_seconds(matcher2TtlSec);
- activation->set_activation_type(ACTIVATE_IMMEDIATELY);
- activation->set_deactivation_atom_matcher_id(matcher1Id);
- activation = metricActivation.add_event_activation();
- activation->set_atom_matcher_id(matcher3Id);
- activation->set_ttl_seconds(matcher3TtlSec);
- activation->set_activation_type(ACTIVATE_ON_BOOT);
- activation->set_deactivation_atom_matcher_id(matcher1Id);
- activation = metricActivation.add_event_activation();
- activation->set_atom_matcher_id(matcher4Id);
- activation->set_ttl_seconds(matcher4TtlSec);
- activation->set_activation_type(ACTIVATE_IMMEDIATELY);
- activation->set_deactivation_atom_matcher_id(matcher2Id);
- *config.add_metric_activation() = metricActivation;
-
- EXPECT_TRUE(initConfig(config));
-
- // Activate some of the event activations.
- ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
- int64_t matcher2StartNs = 12345;
- oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher2Id], matcher2StartNs);
- int64_t matcher3StartNs = 23456;
- oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher3Id], matcher3StartNs);
- EXPECT_TRUE(oldMetricProducers[0]->isActive());
-
- // Map the matchers and predicates in reverse order to force the indices to change.
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher4Index = 0;
- newAtomMatchingTrackerMap[matcher4Id] = 0;
- const int matcher3Index = 1;
- newAtomMatchingTrackerMap[matcher3Id] = 1;
- const int matcher2Index = 2;
- newAtomMatchingTrackerMap[matcher2Id] = 2;
- const int matcher1Index = 3;
- newAtomMatchingTrackerMap[matcher1Id] = 3;
- // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(4);
- std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
- newAtomMatchingTrackers.begin());
- set<int64_t> replacedMatchers;
-
- unordered_map<int64_t, int> newConditionTrackerMap;
- vector<sp<ConditionTracker>> newConditionTrackers;
- set<int64_t> replacedConditions;
- vector<ConditionState> conditionCache;
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
- newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
- newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
- /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- // Verify event activation/deactivation maps.
- ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3);
- EXPECT_THAT(activationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
- EXPECT_THAT(activationAtomTrackerToMetricMap[matcher3Index], UnorderedElementsAre(0));
- EXPECT_THAT(activationAtomTrackerToMetricMap[matcher4Index], UnorderedElementsAre(0));
- ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 2);
- EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher1Index], UnorderedElementsAre(0, 0));
- EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
- ASSERT_EQ(metricsWithActivation.size(), 1);
- EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(0));
-
- // Verify mEventActivation and mEventDeactivation map of the producer.
- sp<MetricProducer> producer = newMetricProducers[0];
- EXPECT_TRUE(producer->isActive());
- ASSERT_EQ(producer->mEventActivationMap.size(), 3);
- shared_ptr<Activation> matcher2Activation = producer->mEventActivationMap[matcher2Index];
- EXPECT_EQ(matcher2Activation->ttl_ns, matcher2TtlSec * NS_PER_SEC);
- EXPECT_EQ(matcher2Activation->activationType, ACTIVATE_IMMEDIATELY);
- EXPECT_EQ(matcher2Activation->state, kActive);
- EXPECT_EQ(matcher2Activation->start_ns, matcher2StartNs);
- shared_ptr<Activation> matcher3Activation = producer->mEventActivationMap[matcher3Index];
- EXPECT_EQ(matcher3Activation->ttl_ns, matcher3TtlSec * NS_PER_SEC);
- EXPECT_EQ(matcher3Activation->activationType, ACTIVATE_ON_BOOT);
- EXPECT_EQ(matcher3Activation->state, kActiveOnBoot);
- shared_ptr<Activation> matcher4Activation = producer->mEventActivationMap[matcher4Index];
- EXPECT_EQ(matcher4Activation->ttl_ns, matcher4TtlSec * NS_PER_SEC);
- EXPECT_EQ(matcher4Activation->activationType, ACTIVATE_IMMEDIATELY);
- EXPECT_EQ(matcher4Activation->state, kNotActive);
-
- ASSERT_EQ(producer->mEventDeactivationMap.size(), 2);
- EXPECT_THAT(producer->mEventDeactivationMap[matcher1Index],
- UnorderedElementsAre(matcher2Activation, matcher3Activation));
- EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index],
- UnorderedElementsAre(matcher4Activation));
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) {
- StatsdConfig config;
- // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig
- AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
- int64_t matcher1Id = matcher1.id();
- *config.add_atom_matcher() = matcher1;
-
- AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
- int64_t matcher2Id = matcher2.id();
- *config.add_atom_matcher() = matcher2;
-
- AtomMatcher matcher3 = CreateTemperatureAtomMatcher();
- int64_t matcher3Id = matcher3.id();
- *config.add_atom_matcher() = matcher3;
-
- Predicate predicate1 = CreateScreenIsOnPredicate();
- int64_t predicate1Id = predicate1.id();
- *config.add_predicate() = predicate1;
-
- // Add a few count metrics.
- // Will be preserved.
- CountMetric countMetric = createCountMetric("COUNT1", matcher1Id, predicate1Id, {});
- int64_t countMetricId = countMetric.id();
- *config.add_count_metric() = countMetric;
-
- // Will be replaced since matcher2 is replaced.
- EventMetric eventMetric = createEventMetric("EVENT1", matcher2Id, nullopt);
- int64_t eventMetricId = eventMetric.id();
- *config.add_event_metric() = eventMetric;
-
- // Will be replaced because the definition changes - a predicate is added.
- GaugeMetric gaugeMetric = createGaugeMetric("GAUGE1", matcher3Id,
- GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
- int64_t gaugeMetricId = gaugeMetric.id();
- *config.add_gauge_metric() = gaugeMetric;
-
- // Preserved.
- ValueMetric valueMetric = createValueMetric("VALUE1", matcher3, predicate1Id, {});
- int64_t valueMetricId = valueMetric.id();
- *config.add_value_metric() = valueMetric;
-
- // Preserved.
- DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {});
- int64_t durationMetricId = durationMetric.id();
- *config.add_duration_metric() = durationMetric;
-
- EXPECT_TRUE(initConfig(config));
-
- // Used later to ensure the condition wizard is replaced. Get it before doing the update.
- sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
- EXPECT_EQ(oldConditionWizard->getStrongCount(), 6);
-
- // Mark matcher 2 as replaced. Causes eventMetric to be replaced.
- set<int64_t> replacedMatchers;
- replacedMatchers.insert(matcher2Id);
-
- // Add predicate1 as a predicate on gaugeMetric, causing it to be replaced.
- gaugeMetric.set_condition(predicate1Id);
-
- // Map the matchers and predicates in reverse order to force the indices to change.
- std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher3Index = 0;
- newAtomMatchingTrackerMap[matcher3Id] = 0;
- const int matcher2Index = 1;
- newAtomMatchingTrackerMap[matcher2Id] = 1;
- const int matcher1Index = 2;
- newAtomMatchingTrackerMap[matcher1Id] = 2;
- // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(3);
- std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
- newAtomMatchingTrackers.begin());
-
- std::unordered_map<int64_t, int> newConditionTrackerMap;
- const int predicate1Index = 0;
- newConditionTrackerMap[predicate1Id] = 0;
- // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<ConditionTracker>> newConditionTrackers(1);
- std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
- newConditionTrackers.begin());
- vector<ConditionState> conditionCache = {ConditionState::kUnknown};
-
- // The order matters. we parse in the order of: count, duration, event, value, gauge.
- StatsdConfig newConfig;
- *newConfig.add_count_metric() = countMetric;
- const int countMetricIndex = 0;
- *newConfig.add_duration_metric() = durationMetric;
- const int durationMetricIndex = 1;
- *newConfig.add_event_metric() = eventMetric;
- const int eventMetricIndex = 2;
- *newConfig.add_value_metric() = valueMetric;
- const int valueMetricIndex = 3;
- *newConfig.add_gauge_metric() = gaugeMetric;
- const int gaugeMetricIndex = 4;
-
- // Add the predicate since duration metric needs it.
- *newConfig.add_predicate() = predicate1;
-
- // Output data structures to validate.
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
- newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
- newConditionTrackers, conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{},
- /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- unordered_map<int64_t, int> expectedMetricProducerMap = {
- {countMetricId, countMetricIndex}, {durationMetricId, durationMetricIndex},
- {eventMetricId, eventMetricIndex}, {valueMetricId, valueMetricIndex},
- {gaugeMetricId, gaugeMetricIndex},
- };
- EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
-
- EXPECT_EQ(replacedMetrics, set<int64_t>({eventMetricId, gaugeMetricId}));
-
- // Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 5);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
- newMetricProducers[newMetricProducerMap.at(countMetricId)]);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)],
- newMetricProducers[newMetricProducerMap.at(durationMetricId)]);
- EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(valueMetricId)],
- newMetricProducers[newMetricProducerMap.at(valueMetricId)]);
-
- // Make sure replaced metrics are different.
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
- newMetricProducers[newMetricProducerMap.at(eventMetricId)]);
- EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gaugeMetricId)],
- newMetricProducers[newMetricProducerMap.at(gaugeMetricId)]);
-
- // Verify the conditionToMetricMap.
- ASSERT_EQ(conditionToMetricMap.size(), 1);
- const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
- EXPECT_THAT(condition1Metrics,
- UnorderedElementsAre(countMetricIndex, gaugeMetricIndex, valueMetricIndex));
-
- // Verify the trackerToMetricMap.
- ASSERT_EQ(trackerToMetricMap.size(), 3);
- const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
- EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex, durationMetricIndex));
- const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
- EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex));
- const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
- EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex, valueMetricIndex));
-
- // Verify event activation/deactivation maps.
- ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
- ASSERT_EQ(metricsWithActivation.size(), 0);
-
- // Verify tracker indices/ids/conditions are correct.
- EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId);
- EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown);
- EXPECT_EQ(newMetricProducers[durationMetricIndex]->getMetricId(), durationMetricId);
- EXPECT_EQ(newMetricProducers[durationMetricIndex]->mConditionTrackerIndex, -1);
- EXPECT_EQ(newMetricProducers[durationMetricIndex]->mCondition, ConditionState::kTrue);
- EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
- EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
- EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
- EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->getMetricId(), gaugeMetricId);
- EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mConditionTrackerIndex, predicate1Index);
- EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mCondition, ConditionState::kUnknown);
-
- sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
- EXPECT_NE(newConditionWizard, oldConditionWizard);
- EXPECT_EQ(newConditionWizard->getStrongCount(), 6);
- oldMetricProducers.clear();
- // Only reference to the old wizard should be the one in the test.
- EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
-}
-
-TEST_F(ConfigUpdateTest, TestAlertPreserve) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
-
- Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
- *config.add_alert() = alert;
- EXPECT_TRUE(initConfig(config));
-
- UpdateStatus updateStatus = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
- /*replacedMetrics*/ {}, updateStatus));
- EXPECT_EQ(updateStatus, UPDATE_PRESERVE);
-}
-
-TEST_F(ConfigUpdateTest, TestAlertMetricChanged) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- CountMetric metric = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
- *config.add_count_metric() = metric;
-
- Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
- *config.add_alert() = alert;
- EXPECT_TRUE(initConfig(config));
-
- UpdateStatus updateStatus = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
- /*replacedMetrics*/ {metric.id()}, updateStatus));
- EXPECT_EQ(updateStatus, UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestAlertDefinitionChanged) {
- StatsdConfig config;
- AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = whatMatcher;
-
- *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
-
- Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
- *config.add_alert() = alert;
- EXPECT_TRUE(initConfig(config));
-
- alert.set_num_buckets(2);
-
- UpdateStatus updateStatus = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
- /*replacedMetrics*/ {}, updateStatus));
- EXPECT_EQ(updateStatus, UPDATE_REPLACE);
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateAlerts) {
- StatsdConfig config;
- // Add atom matchers/predicates/metrics. These are mostly needed for initStatsdConfig
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_predicate() = CreateScreenIsOnPredicate();
-
- CountMetric countMetric = createCountMetric("COUNT1", config.atom_matcher(0).id(), nullopt, {});
- int64_t countMetricId = countMetric.id();
- *config.add_count_metric() = countMetric;
-
- DurationMetric durationMetric =
- createDurationMetric("DURATION1", config.predicate(0).id(), nullopt, {});
- int64_t durationMetricId = durationMetric.id();
- *config.add_duration_metric() = durationMetric;
-
- // Add alerts.
- // Preserved.
- Alert alert1 = createAlert("Alert1", durationMetricId, /*buckets*/ 1, /*triggerSum*/ 5000);
- int64_t alert1Id = alert1.id();
- *config.add_alert() = alert1;
-
- // Replaced.
- Alert alert2 = createAlert("Alert2", countMetricId, /*buckets*/ 1, /*triggerSum*/ 2);
- int64_t alert2Id = alert2.id();
- *config.add_alert() = alert2;
-
- // Replaced.
- Alert alert3 = createAlert("Alert3", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 5000);
- int64_t alert3Id = alert3.id();
- *config.add_alert() = alert3;
-
- // Add Subscriptions.
- Subscription subscription1 = createSubscription("S1", Subscription::ALERT, alert1Id);
- *config.add_subscription() = subscription1;
- Subscription subscription2 = createSubscription("S2", Subscription::ALERT, alert1Id);
- *config.add_subscription() = subscription2;
- Subscription subscription3 = createSubscription("S3", Subscription::ALERT, alert2Id);
- *config.add_subscription() = subscription3;
-
- EXPECT_TRUE(initConfig(config));
-
- // Add a duration tracker to the duration metric to ensure durationTrackers are updated
- // with the proper anomalyTrackers.
- unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
- timeBaseNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- oldMetricProducers[1]->onMatchedLogEvent(0, *event.get());
-
- // Change the count metric. Causes alert2 to be replaced.
- config.mutable_count_metric(0)->set_bucket(ONE_DAY);
- // Change num buckets on alert3, causing replacement.
- alert3.set_num_buckets(5);
-
- // New alert.
- Alert alert4 = createAlert("Alert4", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 10000);
- int64_t alert4Id = alert4.id();
-
- // Move subscription2 to be on alert2 and make a new subscription.
- subscription2.set_rule_id(alert2Id);
- Subscription subscription4 = createSubscription("S4", Subscription::ALERT, alert2Id);
-
- // Create the new config. Modify the old one to avoid adding the matchers/predicates.
- // Add alerts in different order so the map is changed.
- config.clear_alert();
- *config.add_alert() = alert4;
- const int alert4Index = 0;
- *config.add_alert() = alert3;
- const int alert3Index = 1;
- *config.add_alert() = alert1;
- const int alert1Index = 2;
- *config.add_alert() = alert2;
- const int alert2Index = 3;
-
- // Subscription3 is removed.
- config.clear_subscription();
- *config.add_subscription() = subscription4;
- *config.add_subscription() = subscription2;
- *config.add_subscription() = subscription1;
-
- // Output data structures from update metrics. Don't care about the outputs besides
- // replacedMetrics, but need to do this so that the metrics clear their anomaly trackers.
- unordered_map<int64_t, int> newMetricProducerMap;
- vector<sp<MetricProducer>> newMetricProducers;
- unordered_map<int, vector<int>> conditionToMetricMap;
- unordered_map<int, vector<int>> trackerToMetricMap;
- set<int64_t> noReportMetricIds;
- unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
- vector<int> metricsWithActivation;
- set<int64_t> replacedMetrics;
- EXPECT_TRUE(updateMetrics(
- key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
- oldAtomMatchingTrackerMap, oldAtomMatchingTrackerMap, /*replacedMatchers*/ {},
- oldAtomMatchingTrackers, oldConditionTrackerMap, /*replacedConditions=*/{},
- oldConditionTrackers, {ConditionState::kUnknown}, /*stateAtomIdMap*/ {},
- /*allStateGroupMaps=*/{},
- /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
- newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
- activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, replacedMetrics));
-
- EXPECT_EQ(replacedMetrics, set<int64_t>({countMetricId}));
-
- unordered_map<int64_t, int> newAlertTrackerMap;
- vector<sp<AnomalyTracker>> newAnomalyTrackers;
- EXPECT_TRUE(updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap,
- oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers,
- newAlertTrackerMap, newAnomalyTrackers));
-
- unordered_map<int64_t, int> expectedAlertMap = {
- {alert1Id, alert1Index},
- {alert2Id, alert2Index},
- {alert3Id, alert3Index},
- {alert4Id, alert4Index},
- };
- EXPECT_THAT(newAlertTrackerMap, ContainerEq(expectedAlertMap));
-
- // Make sure preserved alerts are the same.
- ASSERT_EQ(newAnomalyTrackers.size(), 4);
- EXPECT_EQ(oldAnomalyTrackers[oldAlertTrackerMap.at(alert1Id)],
- newAnomalyTrackers[newAlertTrackerMap.at(alert1Id)]);
-
- // Make sure replaced alerts are different.
- EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert2Id)],
- newAnomalyTrackers[newAlertTrackerMap.at(alert2Id)]);
- EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert3Id)],
- newAnomalyTrackers[newAlertTrackerMap.at(alert3Id)]);
-
- // Verify the alerts have the correct anomaly trackers.
- ASSERT_EQ(newMetricProducers.size(), 2);
- EXPECT_THAT(newMetricProducers[0]->mAnomalyTrackers,
- UnorderedElementsAre(newAnomalyTrackers[alert2Index]));
- // For durationMetric, make sure the duration trackers get the updated anomalyTrackers.
- DurationMetricProducer* durationProducer =
- static_cast<DurationMetricProducer*>(newMetricProducers[1].get());
- EXPECT_THAT(
- durationProducer->mAnomalyTrackers,
- UnorderedElementsAre(newAnomalyTrackers[alert1Index], newAnomalyTrackers[alert3Index],
- newAnomalyTrackers[alert4Index]));
- ASSERT_EQ(durationProducer->mCurrentSlicedDurationTrackerMap.size(), 1);
- for (const auto& durationTrackerIt : durationProducer->mCurrentSlicedDurationTrackerMap) {
- EXPECT_EQ(durationTrackerIt.second->mAnomalyTrackers, durationProducer->mAnomalyTrackers);
- }
-
- // Verify alerts have the correct subscriptions. Use subscription id as proxy for equivalency.
- vector<int64_t> alert1Subscriptions;
- for (const Subscription& subscription : newAnomalyTrackers[alert1Index]->mSubscriptions) {
- alert1Subscriptions.push_back(subscription.id());
- }
- EXPECT_THAT(alert1Subscriptions, UnorderedElementsAre(subscription1.id()));
- vector<int64_t> alert2Subscriptions;
- for (const Subscription& subscription : newAnomalyTrackers[alert2Index]->mSubscriptions) {
- alert2Subscriptions.push_back(subscription.id());
- }
- EXPECT_THAT(alert2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription4.id()));
- EXPECT_THAT(newAnomalyTrackers[alert3Index]->mSubscriptions, IsEmpty());
- EXPECT_THAT(newAnomalyTrackers[alert4Index]->mSubscriptions, IsEmpty());
-}
-
-TEST_F(ConfigUpdateTest, TestUpdateAlarms) {
- StatsdConfig config;
- // Add alarms.
- Alarm alarm1 = createAlarm("Alarm1", /*offset*/ 1 * MS_PER_SEC, /*period*/ 50 * MS_PER_SEC);
- int64_t alarm1Id = alarm1.id();
- *config.add_alarm() = alarm1;
-
- Alarm alarm2 = createAlarm("Alarm2", /*offset*/ 1 * MS_PER_SEC, /*period*/ 2000 * MS_PER_SEC);
- int64_t alarm2Id = alarm2.id();
- *config.add_alarm() = alarm2;
-
- Alarm alarm3 = createAlarm("Alarm3", /*offset*/ 10 * MS_PER_SEC, /*period*/ 5000 * MS_PER_SEC);
- int64_t alarm3Id = alarm3.id();
- *config.add_alarm() = alarm3;
-
- // Add Subscriptions.
- Subscription subscription1 = createSubscription("S1", Subscription::ALARM, alarm1Id);
- *config.add_subscription() = subscription1;
- Subscription subscription2 = createSubscription("S2", Subscription::ALARM, alarm1Id);
- *config.add_subscription() = subscription2;
- Subscription subscription3 = createSubscription("S3", Subscription::ALARM, alarm2Id);
- *config.add_subscription() = subscription3;
-
- EXPECT_TRUE(initConfig(config));
-
- ASSERT_EQ(oldAlarmTrackers.size(), 3);
- // Config is created at statsd start time, so just add the offsets.
- EXPECT_EQ(oldAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
- EXPECT_EQ(oldAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
- EXPECT_EQ(oldAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
-
- // Change alarm2/alarm3.
- config.mutable_alarm(1)->set_offset_millis(5 * MS_PER_SEC);
- config.mutable_alarm(2)->set_period_millis(10000 * MS_PER_SEC);
-
- // Move subscription2 to be on alarm2 and make a new subscription.
- config.mutable_subscription(1)->set_rule_id(alarm2Id);
- Subscription subscription4 = createSubscription("S4", Subscription::ALARM, alarm1Id);
- *config.add_subscription() = subscription4;
-
- // Update time is 2 seconds after the base time.
- int64_t currentTimeNs = timeBaseNs + 2 * NS_PER_SEC;
- vector<sp<AlarmTracker>> newAlarmTrackers;
- EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
- newAlarmTrackers));
-
- ASSERT_EQ(newAlarmTrackers.size(), 3);
- // Config is updated 2 seconds after statsd start
- // The offset has passed for alarm1, but not for alarms 2/3.
- EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 50);
- EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5);
- EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
-
- // Verify alarms have the correct subscriptions. Use subscription id as proxy for equivalency.
- vector<int64_t> alarm1Subscriptions;
- for (const Subscription& subscription : newAlarmTrackers[0]->mSubscriptions) {
- alarm1Subscriptions.push_back(subscription.id());
- }
- EXPECT_THAT(alarm1Subscriptions, UnorderedElementsAre(subscription1.id(), subscription4.id()));
- vector<int64_t> alarm2Subscriptions;
- for (const Subscription& subscription : newAlarmTrackers[1]->mSubscriptions) {
- alarm2Subscriptions.push_back(subscription.id());
- }
- EXPECT_THAT(alarm2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription3.id()));
- EXPECT_THAT(newAlarmTrackers[2]->mSubscriptions, IsEmpty());
-
- // Verify the alarm monitor is updated accordingly once the old alarms are removed.
- // Alarm2 fires the earliest.
- oldAlarmTrackers.clear();
- EXPECT_EQ(periodicAlarmMonitor->getRegisteredAlarmTimeSec(), timeBaseNs / NS_PER_SEC + 5);
-
- // Do another update 60 seconds after config creation time, after the offsets of each alarm.
- currentTimeNs = timeBaseNs + 60 * NS_PER_SEC;
- newAlarmTrackers.clear();
- EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
- newAlarmTrackers));
-
- ASSERT_EQ(newAlarmTrackers.size(), 3);
- // Config is updated one minute after statsd start.
- // Two periods have passed for alarm 1, one has passed for alarms2/3.
- EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 2 * 50);
- EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5 + 2000);
- EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10 + 10000);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
deleted file mode 100644
index 9e2350b33018..000000000000
--- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
+++ /dev/null
@@ -1,890 +0,0 @@
-// Copyright (C) 2020 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 "src/metrics/parsing_utils/metrics_manager_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <private/android_filesystem_config.h>
-#include <stdio.h>
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "src/condition/ConditionTracker.h"
-#include "src/matchers/AtomMatchingTracker.h"
-#include "src/metrics/CountMetricProducer.h"
-#include "src/metrics/DurationMetricProducer.h"
-#include "src/metrics/GaugeMetricProducer.h"
-#include "src/metrics/MetricProducer.h"
-#include "src/metrics/ValueMetricProducer.h"
-#include "src/state/StateManager.h"
-#include "tests/metrics/metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
-
-using namespace testing;
-using android::sp;
-using android::os::statsd::Predicate;
-using std::map;
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-const ConfigKey kConfigKey(0, 12345);
-const long kAlertId = 3;
-
-const long timeBaseSec = 1000;
-
-StatsdConfig buildGoodConfig() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- combination->add_matcher(StringToId("SCREEN_IS_OFF"));
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("SCREEN_IS_ON"));
- metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
- config.add_no_report_metric(3);
-
- auto alert = config.add_alert();
- alert->set_id(kAlertId);
- alert->set_metric_id(3);
- alert->set_num_buckets(10);
- alert->set_refractory_period_secs(100);
- alert->set_trigger_if_sum_gt(100);
- return config;
-}
-
-StatsdConfig buildCircleMatchers() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- // Circle dependency
- combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
-
- return config;
-}
-
-StatsdConfig buildAlertWithUnknownMetric() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("SCREEN_IS_ON"));
- metric->set_bucket(ONE_MINUTE);
- metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
- auto alert = config.add_alert();
- alert->set_id(3);
- alert->set_metric_id(2);
- alert->set_num_buckets(10);
- alert->set_refractory_period_secs(100);
- alert->set_trigger_if_sum_gt(100);
- return config;
-}
-
-StatsdConfig buildMissingMatchers() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("SCREEN_IS_ON"));
- // undefined matcher
- combination->add_matcher(StringToId("ABC"));
-
- return config;
-}
-
-StatsdConfig buildMissingPredicate() {
- StatsdConfig config;
- config.set_id(12345);
-
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("SCREEN_EVENT"));
- metric->set_bucket(ONE_MINUTE);
- metric->set_condition(StringToId("SOME_CONDITION"));
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_EVENT"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2);
-
- return config;
-}
-
-StatsdConfig buildDimensionMetricsWithMultiTags() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(3);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("BATTERY_LOW"));
-
- AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
- combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
-
- // Count process state changes, slice by uid, while SCREEN_IS_OFF
- CountMetric* metric = config.add_count_metric();
- metric->set_id(3);
- metric->set_what(StringToId("BATTERY_LOW"));
- metric->set_bucket(ONE_MINUTE);
- // This case is interesting. We want to dimension across two atoms.
- metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
- auto alert = config.add_alert();
- alert->set_id(kAlertId);
- alert->set_metric_id(3);
- alert->set_num_buckets(10);
- alert->set_refractory_period_secs(100);
- alert->set_trigger_if_sum_gt(100);
- return config;
-}
-
-StatsdConfig buildCirclePredicates() {
- StatsdConfig config;
- config.set_id(12345);
-
- AtomMatcher* eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
- SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
- eventMatcher = config.add_atom_matcher();
- eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
- simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
- auto condition = config.add_predicate();
- condition->set_id(StringToId("SCREEN_IS_ON"));
- SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
- simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
- simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
-
- condition = config.add_predicate();
- condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
- Predicate_Combination* combination = condition->mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_predicate(StringToId("SCREEN_IS_ON"));
- combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
- return config;
-}
-
-StatsdConfig buildConfigWithDifferentPredicates() {
- StatsdConfig config;
- config.set_id(12345);
-
- auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE);
- *config.add_atom_matcher() = pulledAtomMatcher;
- auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = screenOnAtomMatcher;
- auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = screenOffAtomMatcher;
- auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher();
- *config.add_atom_matcher() = batteryNoneAtomMatcher;
- auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher();
- *config.add_atom_matcher() = batteryUsbAtomMatcher;
-
- // Simple condition with InitialValue set to default (unknown).
- auto screenOnUnknownPredicate = CreateScreenIsOnPredicate();
- *config.add_predicate() = screenOnUnknownPredicate;
-
- // Simple condition with InitialValue set to false.
- auto screenOnFalsePredicate = config.add_predicate();
- screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse"));
- SimplePredicate* simpleScreenOnFalsePredicate =
- screenOnFalsePredicate->mutable_simple_predicate();
- simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id());
- simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id());
- simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
- // Simple condition with InitialValue set to false.
- auto onBatteryFalsePredicate = config.add_predicate();
- onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse"));
- SimplePredicate* simpleOnBatteryFalsePredicate =
- onBatteryFalsePredicate->mutable_simple_predicate();
- simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id());
- simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id());
- simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
- // Combination condition with both simple condition InitialValues set to false.
- auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate();
- screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse"));
- screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation(
- LogicalOperation::AND);
- addPredicateToPredicateCombination(*screenOnFalsePredicate,
- screenOnFalseOnBatteryFalsePredicate);
- addPredicateToPredicateCombination(*onBatteryFalsePredicate,
- screenOnFalseOnBatteryFalsePredicate);
-
- // Combination condition with one simple condition InitialValue set to unknown and one set to
- // false.
- auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate();
- screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse"));
- screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation(
- LogicalOperation::AND);
- addPredicateToPredicateCombination(screenOnUnknownPredicate,
- screenOnUnknownOnBatteryFalsePredicate);
- addPredicateToPredicateCombination(*onBatteryFalsePredicate,
- screenOnUnknownOnBatteryFalsePredicate);
-
- // Simple condition metric with initial value false.
- ValueMetric* metric1 = config.add_value_metric();
- metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse"));
- metric1->set_what(pulledAtomMatcher.id());
- *metric1->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric1->set_bucket(FIVE_MINUTES);
- metric1->set_condition(screenOnFalsePredicate->id());
-
- // Simple condition metric with initial value unknown.
- ValueMetric* metric2 = config.add_value_metric();
- metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown"));
- metric2->set_what(pulledAtomMatcher.id());
- *metric2->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric2->set_bucket(FIVE_MINUTES);
- metric2->set_condition(screenOnUnknownPredicate.id());
-
- // Combination condition metric with initial values false and false.
- ValueMetric* metric3 = config.add_value_metric();
- metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse"));
- metric3->set_what(pulledAtomMatcher.id());
- *metric3->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric3->set_bucket(FIVE_MINUTES);
- metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id());
-
- // Combination condition metric with initial values unknown and false.
- ValueMetric* metric4 = config.add_value_metric();
- metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse"));
- metric4->set_what(pulledAtomMatcher.id());
- *metric4->mutable_value_field() =
- CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
- metric4->set_bucket(FIVE_MINUTES);
- metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id());
-
- return config;
-}
-} // anonymous namespace
-
-TEST(MetricsManagerTest, TestInitialConditions) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildConfigWithDifferentPredicates();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_TRUE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
- ASSERT_EQ(4u, allMetricProducers.size());
- ASSERT_EQ(5u, allConditionTrackers.size());
-
- ConditionKey queryKey;
- vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
-
- allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
- allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
- EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[1]);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[2]);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[3]);
- EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]);
-
- EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition);
- EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition);
- EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition);
-}
-
-TEST(MetricsManagerTest, TestGoodConfig) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildGoodConfig();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_TRUE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
- ASSERT_EQ(1u, allMetricProducers.size());
- EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0)));
- ASSERT_EQ(1u, allAnomalyTrackers.size());
- ASSERT_EQ(1u, noReportMetricIds.size());
- ASSERT_EQ(1u, alertTrackerMap.size());
- EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
- EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
-}
-
-TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildDimensionMetricsWithMultiTags();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildCircleMatchers();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingMatchers) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildMissingMatchers();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingPredicate) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildMissingPredicate();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
- EXPECT_FALSE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCirclePredicateDependency) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildCirclePredicates();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
- sp<UidMap> uidMap = new UidMap();
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- sp<AlarmMonitor> anomalyAlarmMonitor;
- sp<AlarmMonitor> periodicAlarmMonitor;
- StatsdConfig config = buildAlertWithUnknownMetric();
- set<int> allTagIds;
- vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
- unordered_map<int64_t, int> atomMatchingTrackerMap;
- vector<sp<ConditionTracker>> allConditionTrackers;
- unordered_map<int64_t, int> conditionTrackerMap;
- vector<sp<MetricProducer>> allMetricProducers;
- unordered_map<int64_t, int> metricProducerMap;
- std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
- std::vector<sp<AlarmTracker>> allAlarmTrackers;
- unordered_map<int, std::vector<int>> conditionToMetricMap;
- unordered_map<int, std::vector<int>> trackerToMetricMap;
- unordered_map<int, std::vector<int>> trackerToConditionMap;
- unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
- unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
- unordered_map<int64_t, int> alertTrackerMap;
- vector<int> metricsWithActivation;
- map<int64_t, uint64_t> stateProtoHashes;
- std::set<int64_t> noReportMetricIds;
-
- EXPECT_FALSE(initStatsdConfig(
- kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
- allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
- allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
- stateProtoHashes, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) {
- sp<UidMap> uidMap = new UidMap();
- AtomMatcher matcher;
- // Matcher has no contents_case (simple/combination), so it is invalid.
- matcher.set_id(21);
- EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr);
-}
-
-TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple) {
- int index = 1;
- int64_t id = 123;
- sp<UidMap> uidMap = new UidMap();
- AtomMatcher matcher;
- matcher.set_id(id);
- SimpleAtomMatcher* simpleAtomMatcher = matcher.mutable_simple_atom_matcher();
- simpleAtomMatcher->set_atom_id(util::SCREEN_STATE_CHANGED);
- simpleAtomMatcher->add_field_value_matcher()->set_field(
- 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-
- sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap);
- EXPECT_NE(tracker, nullptr);
-
- EXPECT_TRUE(tracker->mInitialized);
- EXPECT_EQ(tracker->getId(), id);
- EXPECT_EQ(tracker->mIndex, index);
- const set<int>& atomIds = tracker->getAtomIds();
- ASSERT_EQ(atomIds.size(), 1);
- EXPECT_EQ(atomIds.count(util::SCREEN_STATE_CHANGED), 1);
-}
-
-TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) {
- int index = 1;
- int64_t id = 123;
- sp<UidMap> uidMap = new UidMap();
- AtomMatcher matcher;
- matcher.set_id(id);
- AtomMatcher_Combination* combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::OR);
- combination->add_matcher(123);
- combination->add_matcher(223);
-
- sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap);
- EXPECT_NE(tracker, nullptr);
-
- // Combination matchers need to be initialized first.
- EXPECT_FALSE(tracker->mInitialized);
- EXPECT_EQ(tracker->getId(), id);
- EXPECT_EQ(tracker->mIndex, index);
- const set<int>& atomIds = tracker->getAtomIds();
- ASSERT_EQ(atomIds.size(), 0);
-}
-
-TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) {
- const ConfigKey key(123, 456);
- // Predicate has no contents_case (simple/combination), so it is invalid.
- Predicate predicate;
- predicate.set_id(21);
- unordered_map<int64_t, int> atomTrackerMap;
- EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr);
-}
-
-TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) {
- int index = 1;
- int64_t id = 987;
- const ConfigKey key(123, 456);
-
- int startMatcherIndex = 2, stopMatcherIndex = 0, stopAllMatcherIndex = 1;
- int64_t startMatcherId = 246, stopMatcherId = 153, stopAllMatcherId = 975;
-
- Predicate predicate;
- predicate.set_id(id);
- SimplePredicate* simplePredicate = predicate.mutable_simple_predicate();
- simplePredicate->set_start(startMatcherId);
- simplePredicate->set_stop(stopMatcherId);
- simplePredicate->set_stop_all(stopAllMatcherId);
-
- unordered_map<int64_t, int> atomTrackerMap;
- atomTrackerMap[startMatcherId] = startMatcherIndex;
- atomTrackerMap[stopMatcherId] = stopMatcherIndex;
- atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex;
-
- sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
- EXPECT_EQ(tracker->getConditionId(), id);
- EXPECT_EQ(tracker->isSliced(), false);
- EXPECT_TRUE(tracker->IsSimpleCondition());
- const set<int>& interestedMatchers = tracker->getAtomMatchingTrackerIndex();
- ASSERT_EQ(interestedMatchers.size(), 3);
- ASSERT_EQ(interestedMatchers.count(startMatcherIndex), 1);
- ASSERT_EQ(interestedMatchers.count(stopMatcherIndex), 1);
- ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1);
-}
-
-TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) {
- int index = 1;
- int64_t id = 987;
- const ConfigKey key(123, 456);
-
- Predicate predicate;
- predicate.set_id(id);
- Predicate_Combination* combinationPredicate = predicate.mutable_combination();
- combinationPredicate->set_operation(LogicalOperation::AND);
- combinationPredicate->add_predicate(888);
- combinationPredicate->add_predicate(777);
-
- // Combination conditions must be initialized to set most state.
- unordered_map<int64_t, int> atomTrackerMap;
- sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
- EXPECT_EQ(tracker->getConditionId(), id);
- EXPECT_FALSE(tracker->IsSimpleCondition());
-}
-
-TEST(MetricsManagerTest, TestCreateAnomalyTrackerInvalidMetric) {
- Alert alert;
- alert.set_id(123);
- alert.set_metric_id(1);
- alert.set_trigger_if_sum_gt(1);
- alert.set_num_buckets(1);
-
- sp<AlarmMonitor> anomalyAlarmMonitor;
- vector<sp<MetricProducer>> metricProducers;
- // Pass in empty metric producers, causing an error.
- EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {}, metricProducers), nullopt);
-}
-
-TEST(MetricsManagerTest, TestCreateAnomalyTrackerNoThreshold) {
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(123);
- alert.set_metric_id(metricId);
- alert.set_num_buckets(1);
-
- CountMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
- kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
- sp<AlarmMonitor> anomalyAlarmMonitor;
- EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
-}
-
-TEST(MetricsManagerTest, TestCreateAnomalyTrackerMissingBuckets) {
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(123);
- alert.set_metric_id(metricId);
- alert.set_trigger_if_sum_gt(1);
-
- CountMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
- kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
- sp<AlarmMonitor> anomalyAlarmMonitor;
- EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
-}
-
-TEST(MetricsManagerTest, TestCreateAnomalyTrackerGood) {
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(123);
- alert.set_metric_id(metricId);
- alert.set_trigger_if_sum_gt(1);
- alert.set_num_buckets(1);
-
- CountMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
- kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
- sp<AlarmMonitor> anomalyAlarmMonitor;
- EXPECT_NE(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
-}
-
-TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) {
- int64_t metricId = 1;
- Alert alert;
- alert.set_id(123);
- alert.set_metric_id(metricId);
- // Impossible for alert to fire since the time is bigger than bucketSize * numBuckets
- alert.set_trigger_if_sum_gt(MillisToNano(TimeUnitToBucketSizeInMillis(ONE_MINUTE)) + 1);
- alert.set_num_buckets(1);
-
- DurationMetric metric;
- metric.set_id(metricId);
- metric.set_bucket(ONE_MINUTE);
- metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
- FieldMatcher dimensions;
- sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer(
- kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, 0x0123456789, dimensions, 0, 0)});
- sp<AlarmMonitor> anomalyAlarmMonitor;
- EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
deleted file mode 100644
index 1ba8593231b0..000000000000
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/shell/ShellSubscriber.h"
-
-#include <gtest/gtest.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <vector>
-
-#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
-#include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
-#include "frameworks/proto_logging/stats/atoms.pb.h"
-#include "stats_event.h"
-#include "tests/metrics/metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
-
-using namespace android::os::statsd;
-using android::sp;
-using std::vector;
-using testing::_;
-using testing::Invoke;
-using testing::NaggyMock;
-using testing::StrictMock;
-
-#ifdef __ANDROID__
-
-void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
- sp<MockStatsPullerManager> pullerManager,
- const vector<std::shared_ptr<LogEvent>>& pushedEvents,
- const ShellData& expectedData) {
- // set up 2 pipes for read/write config and data
- int fds_config[2];
- ASSERT_EQ(0, pipe(fds_config));
-
- int fds_data[2];
- ASSERT_EQ(0, pipe(fds_data));
-
- size_t bufferSize = config.ByteSize();
- // write the config to pipe, first write size of the config
- write(fds_config[1], &bufferSize, sizeof(bufferSize));
- // then write config itself
- vector<uint8_t> buffer(bufferSize);
- config.SerializeToArray(&buffer[0], bufferSize);
- write(fds_config[1], buffer.data(), bufferSize);
- close(fds_config[1]);
-
- sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
-
- // mimic a binder thread that a shell subscriber runs on. it would block.
- std::thread reader([&shellClient, &fds_config, &fds_data] {
- shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1);
- });
- reader.detach();
-
- // let the shell subscriber to receive the config from pipe.
- std::this_thread::sleep_for(100ms);
-
- if (pushedEvents.size() > 0) {
- // send a log event that matches the config.
- std::thread log_reader([&shellClient, &pushedEvents] {
- for (const auto& event : pushedEvents) {
- shellClient->onLogEvent(*event);
- }
- });
-
- log_reader.detach();
-
- if (log_reader.joinable()) {
- log_reader.join();
- }
- }
-
- // wait for the data to be written.
- std::this_thread::sleep_for(100ms);
-
- // Because we might receive heartbeats from statsd, consisting of data sizes
- // of 0, encapsulate reads within a while loop.
- bool readAtom = false;
- while (!readAtom) {
- // Read the atom size.
- size_t dataSize = 0;
- read(fds_data[0], &dataSize, sizeof(dataSize));
- if (dataSize == 0) continue;
- EXPECT_EQ(expectedData.ByteSize(), int(dataSize));
-
- // Read that much data in proto binary format.
- vector<uint8_t> dataBuffer(dataSize);
- EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
-
- // Make sure the received bytes can be parsed to an atom.
- ShellData receivedAtom;
- EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
-
- // Serialize the expected atom to byte array and compare to make sure
- // they are the same.
- vector<uint8_t> expectedAtomBuffer(expectedData.ByteSize());
- expectedData.SerializeToArray(expectedAtomBuffer.data(), expectedData.ByteSize());
- EXPECT_EQ(expectedAtomBuffer, dataBuffer);
-
- readAtom = true;
- }
-
- close(fds_data[0]);
- if (reader.joinable()) {
- reader.join();
- }
-}
-
-TEST(ShellSubscriberTest, testPushedSubscription) {
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- vector<std::shared_ptr<LogEvent>> pushedList;
-
- // Create the LogEvent from an AStatsEvent
- std::unique_ptr<LogEvent> logEvent = CreateScreenStateChangedEvent(
- 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- pushedList.push_back(std::move(logEvent));
-
- // create a simple config to get screen events
- ShellSubscription config;
- config.add_pushed()->set_atom_id(29);
-
- // this is the expected screen event atom.
- ShellData shellData;
- shellData.add_atom()->mutable_screen_state_changed()->set_state(
- ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-
- runShellTest(config, uidMap, pullerManager, pushedList, shellData);
-}
-
-namespace {
-
-int kUid1 = 1000;
-int kUid2 = 2000;
-
-int kCpuTime1 = 100;
-int kCpuTime2 = 200;
-
-ShellData getExpectedShellData() {
- ShellData shellData;
- auto* atom1 = shellData.add_atom()->mutable_cpu_active_time();
- atom1->set_uid(kUid1);
- atom1->set_time_millis(kCpuTime1);
-
- auto* atom2 = shellData.add_atom()->mutable_cpu_active_time();
- atom2->set_uid(kUid2);
- atom2->set_time_millis(kCpuTime2);
-
- return shellData;
-}
-
-ShellSubscription getPulledConfig() {
- ShellSubscription config;
- auto* pull_config = config.add_pulled();
- pull_config->mutable_matcher()->set_atom_id(10016);
- pull_config->set_freq_millis(2000);
- return config;
-}
-
-shared_ptr<LogEvent> makeCpuActiveTimeAtom(int32_t uid, int64_t timeMillis) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, 10016);
- AStatsEvent_overwriteTimestamp(statsEvent, 1111L);
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeInt64(statsEvent, timeMillis);
-
- std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-} // namespace
-
-TEST(ShellSubscriberTest, testPulledSubscription) {
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- const vector<int32_t> uids = {AID_SYSTEM};
- EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _))
- .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
- vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
- data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
- return true;
- }));
- runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
- getExpectedShellData());
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
deleted file mode 100644
index 6516c1529514..000000000000
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * 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 "state/StateTracker.h"
-
-#include <gtest/gtest.h>
-#include <private/android_filesystem_config.h>
-
-#include "state/StateListener.h"
-#include "state/StateManager.h"
-#include "state/StateTracker.h"
-#include "stats_event.h"
-#include "tests/statsd_test_util.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-const int32_t timestampNs = 1000;
-
-/**
- * Mock StateListener class for testing.
- * Stores primary key and state pairs.
- */
-class TestStateListener : public virtual StateListener {
-public:
- TestStateListener(){};
-
- virtual ~TestStateListener(){};
-
- struct Update {
- Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){};
- HashableDimensionKey mKey;
- int mState;
- };
-
- std::vector<Update> updates;
-
- void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
- const HashableDimensionKey& primaryKey, const FieldValue& oldState,
- const FieldValue& newState) {
- updates.emplace_back(primaryKey, newState.mValue.int_value);
- }
-};
-
-int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& queryKey) {
- FieldValue output;
- mgr.getStateValue(atomId, queryKey, &output);
- return output.mValue.int_value;
-}
-
-// START: build event functions.
-// Incorrect event - missing fields
-std::unique_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
- int state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, 1000);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeString(statsEvent, packageName.c_str());
- // Missing field 3 - using_alert_window.
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-// Incorrect event - exclusive state has wrong type
-std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, 1000);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeString(statsEvent, packageName.c_str());
- AStatsEvent_writeInt32(statsEvent, true); // using_alert_window
- AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-// END: build event functions.
-
-TEST(StateListenerTest, TestStateListenerWeakPointer) {
- sp<TestStateListener> listener = new TestStateListener();
- wp<TestStateListener> wListener = listener;
- listener = nullptr; // let go of listener
- EXPECT_TRUE(wListener.promote() == nullptr);
-}
-
-TEST(StateManagerTest, TestStateManagerGetInstance) {
- sp<TestStateListener> listener1 = new TestStateListener();
- StateManager& mgr = StateManager::getInstance();
- mgr.clear();
-
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
-}
-
-TEST(StateManagerTest, TestOnLogEvent) {
- sp<MockUidMap> uidMap = makeMockUidMapForPackage("com.android.systemui", {10111});
- sp<TestStateListener> listener1 = new TestStateListener();
- StateManager mgr;
- mgr.updateLogSources(uidMap);
- // Add StateTracker by registering a listener.
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
-
- // log event using AID_ROOT
- std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
- timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- mgr.onLogEvent(*event);
-
- // check StateTracker was updated by querying for state
- HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
-
- // log event using mocked uid
- event = CreateScreenStateChangedEvent(
- timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_OFF, 10111);
- mgr.onLogEvent(*event);
-
- // check StateTracker was updated by querying for state
- queryKey = DEFAULT_DIMENSION_KEY;
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
-
- // log event using non-whitelisted uid
- event = CreateScreenStateChangedEvent(timestampNs,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON, 10112);
- mgr.onLogEvent(*event);
-
- // check StateTracker was NOT updated by querying for state
- queryKey = DEFAULT_DIMENSION_KEY;
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
-
- // log event using AID_SYSTEM
- event = CreateScreenStateChangedEvent(
- timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON, AID_SYSTEM);
- mgr.onLogEvent(*event);
-
- // check StateTracker was updated by querying for state
- queryKey = DEFAULT_DIMENSION_KEY;
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
-}
-
-/**
- * Test registering listeners to StateTrackers
- *
- * - StateManager will create a new StateTracker if it doesn't already exist
- * and then register the listener to the StateTracker.
- * - If a listener is already registered to a StateTracker, it is not added again.
- * - StateTrackers are only created for atoms that are state atoms.
- */
-TEST(StateTrackerTest, TestRegisterListener) {
- sp<TestStateListener> listener1 = new TestStateListener();
- sp<TestStateListener> listener2 = new TestStateListener();
- StateManager mgr;
-
- // Register listener to non-existing StateTracker
- EXPECT_EQ(0, mgr.getStateTrackersCount());
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
-
- // Register listener to existing StateTracker
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
-
- // Register already registered listener to existing StateTracker
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
-
- // Register listener to non-state atom
- mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2);
- EXPECT_EQ(2, mgr.getStateTrackersCount());
-}
-
-/**
- * Test unregistering listeners from StateTrackers
- *
- * - StateManager will unregister listeners from a StateTracker only if the
- * StateTracker exists and the listener is registered to the StateTracker.
- * - Once all listeners are removed from a StateTracker, the StateTracker
- * is also removed.
- */
-TEST(StateTrackerTest, TestUnregisterListener) {
- sp<TestStateListener> listener1 = new TestStateListener();
- sp<TestStateListener> listener2 = new TestStateListener();
- StateManager mgr;
-
- // Unregister listener from non-existing StateTracker
- EXPECT_EQ(0, mgr.getStateTrackersCount());
- mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1);
- EXPECT_EQ(0, mgr.getStateTrackersCount());
- EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
-
- // Unregister non-registered listener from existing StateTracker
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
- mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
-
- // Unregister second-to-last listener from StateTracker
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
- mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
-
- // Unregister last listener from StateTracker
- mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2);
- EXPECT_EQ(0, mgr.getStateTrackersCount());
- EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
-}
-
-/**
- * Test a binary state atom with nested counting.
- *
- * To go from an "ON" state to an "OFF" state with nested counting, we must see
- * an equal number of "OFF" events as "ON" events.
- * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
- * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
- */
-TEST(StateTrackerTest, TestStateChangeNested) {
- sp<TestStateListener> listener = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener);
-
- std::vector<int> attributionUids1 = {1000};
- std::vector<string> attributionTags1 = {"tag"};
-
- std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(timestampNs, attributionUids1,
- attributionTags1, "wakelockName");
- mgr.onLogEvent(*event1);
- ASSERT_EQ(1, listener->updates.size());
- EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
- EXPECT_EQ(1, listener->updates[0].mState);
- listener->updates.clear();
-
- std::unique_ptr<LogEvent> event2 = CreateAcquireWakelockEvent(
- timestampNs + 1000, attributionUids1, attributionTags1, "wakelockName");
- mgr.onLogEvent(*event2);
- ASSERT_EQ(0, listener->updates.size());
-
- std::unique_ptr<LogEvent> event3 = CreateReleaseWakelockEvent(
- timestampNs + 2000, attributionUids1, attributionTags1, "wakelockName");
- mgr.onLogEvent(*event3);
- ASSERT_EQ(0, listener->updates.size());
-
- std::unique_ptr<LogEvent> event4 = CreateReleaseWakelockEvent(
- timestampNs + 3000, attributionUids1, attributionTags1, "wakelockName");
- mgr.onLogEvent(*event4);
- ASSERT_EQ(1, listener->updates.size());
- EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
- EXPECT_EQ(0, listener->updates[0].mState);
-}
-
-/**
- * Test a state atom with a reset state.
- *
- * If the reset state value is seen, every state in the map is set to the default
- * state and every listener is notified.
- */
-TEST(StateTrackerTest, TestStateChangeReset) {
- sp<TestStateListener> listener = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener);
-
- std::vector<int> attributionUids1 = {1000};
- std::vector<string> attributionTags1 = {"tag1"};
- std::vector<int> attributionUids2 = {2000};
-
- std::unique_ptr<LogEvent> event1 =
- CreateBleScanStateChangedEvent(timestampNs, attributionUids1, attributionTags1,
- BleScanStateChanged::ON, false, false, false);
- mgr.onLogEvent(*event1);
- ASSERT_EQ(1, listener->updates.size());
- EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
- EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
- FieldValue stateFieldValue;
- mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
- EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
- listener->updates.clear();
-
- std::unique_ptr<LogEvent> event2 =
- CreateBleScanStateChangedEvent(timestampNs + 1000, attributionUids2, attributionTags1,
- BleScanStateChanged::ON, false, false, false);
- mgr.onLogEvent(*event2);
- ASSERT_EQ(1, listener->updates.size());
- EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
- EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
- mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
- EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
- listener->updates.clear();
-
- std::unique_ptr<LogEvent> event3 =
- CreateBleScanStateChangedEvent(timestampNs + 2000, attributionUids2, attributionTags1,
- BleScanStateChanged::RESET, false, false, false);
- mgr.onLogEvent(*event3);
- ASSERT_EQ(2, listener->updates.size());
- for (const TestStateListener::Update& update : listener->updates) {
- EXPECT_EQ(BleScanStateChanged::OFF, update.mState);
-
- mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, update.mKey, &stateFieldValue);
- EXPECT_EQ(BleScanStateChanged::OFF, stateFieldValue.mValue.int_value);
- }
-}
-
-/**
- * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
- * updates listener for states without primary keys.
- */
-TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
- sp<TestStateListener> listener1 = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
-
- // log event
- std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
- timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- mgr.onLogEvent(*event);
-
- // check listener was updated
- ASSERT_EQ(1, listener1->updates.size());
- EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
- EXPECT_EQ(2, listener1->updates[0].mState);
-
- // check StateTracker was updated by querying for state
- HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
-}
-
-/**
- * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
- * updates listener for states with one primary key.
- */
-TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
- sp<TestStateListener> listener1 = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1);
-
- // log event
- std::unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent(
- timestampNs, 1000 /*uid*/, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
- mgr.onLogEvent(*event);
-
- // check listener was updated
- ASSERT_EQ(1, listener1->updates.size());
- EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
- EXPECT_EQ(1002, listener1->updates[0].mState);
-
- // check StateTracker was updated by querying for state
- HashableDimensionKey queryKey;
- getUidProcessKey(1000 /* uid */, &queryKey);
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
- getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey));
-}
-
-TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
- sp<TestStateListener> listener1 = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1);
-
- // Log event.
- std::vector<int> attributionUids = {1001};
- std::vector<string> attributionTags = {"tag1"};
-
- std::unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timestampNs, attributionUids,
- attributionTags, "wakelockName");
- mgr.onLogEvent(*event);
- EXPECT_EQ(1, mgr.getStateTrackersCount());
- EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED));
-
- // Check listener was updated.
- ASSERT_EQ(1, listener1->updates.size());
- ASSERT_EQ(3, listener1->updates[0].mKey.getValues().size());
- EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
- EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
- EXPECT_EQ("wakelockName", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
- EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
-
- // Check StateTracker was updated by querying for state.
- HashableDimensionKey queryKey;
- getPartialWakelockKey(1001 /* uid */, "wakelockName", &queryKey);
- EXPECT_EQ(WakelockStateChanged::ACQUIRE,
- getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey));
-
- // No state stored for this query key.
- HashableDimensionKey queryKey2;
- getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
- EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
- getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
-
- // Partial query fails.
- HashableDimensionKey queryKey3;
- getPartialWakelockKey(1001 /* uid */, &queryKey3);
- EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
- getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
-}
-
-/**
- * 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(util::OVERLAY_STATE_CHANGED, listener1);
-
- // log event
- std::unique_ptr<LogEvent> event = CreateOverlayStateChangedEvent(
- timestampNs, 1000 /* uid */, "package1", true /*using_alert_window*/,
- OverlayStateChanged::ENTERED);
- mgr.onLogEvent(*event);
-
- // check listener was updated
- ASSERT_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(OverlayStateChanged::ENTERED,
- getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey));
-}
-
-/**
- * Test StateManager's onLogEvent and StateListener's onStateChanged
- * when there is an error extracting state from log event. Listener is not
- * updated of state change.
- */
-TEST(StateTrackerTest, TestStateChangeEventError) {
- sp<TestStateListener> listener1 = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
-
- // log event
- std::shared_ptr<LogEvent> event1 =
- buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
- std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
-
- // check listener was updated
- mgr.onLogEvent(*event1);
- ASSERT_EQ(0, listener1->updates.size());
- mgr.onLogEvent(*event2);
- ASSERT_EQ(0, listener1->updates.size());
-}
-
-TEST(StateTrackerTest, TestStateQuery) {
- sp<TestStateListener> listener1 = new TestStateListener();
- sp<TestStateListener> listener2 = new TestStateListener();
- sp<TestStateListener> listener3 = new TestStateListener();
- sp<TestStateListener> listener4 = new TestStateListener();
- StateManager mgr;
- mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
- mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2);
- mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3);
- mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4);
-
- std::unique_ptr<LogEvent> event1 = CreateUidProcessStateChangedEvent(
- timestampNs, 1000 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
- std::unique_ptr<LogEvent> event2 = CreateUidProcessStateChangedEvent(
- timestampNs + 1000, 1001 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value:
- // 1003
- std::unique_ptr<LogEvent> event3 = CreateUidProcessStateChangedEvent(
- timestampNs + 2000, 1002 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
- std::unique_ptr<LogEvent> event4 = CreateUidProcessStateChangedEvent(
- timestampNs + 3000, 1001 /*uid*/,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
- std::unique_ptr<LogEvent> event5 = CreateScreenStateChangedEvent(
- timestampNs + 4000, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- std::unique_ptr<LogEvent> event6 = CreateOverlayStateChangedEvent(
- timestampNs + 5000, 1000 /*uid*/, "package1", true /*using_alert_window*/,
- OverlayStateChanged::ENTERED);
- std::unique_ptr<LogEvent> event7 = CreateOverlayStateChangedEvent(
- timestampNs + 6000, 1000 /*uid*/, "package2", true /*using_alert_window*/,
- OverlayStateChanged::EXITED);
-
- std::vector<int> attributionUids = {1005};
- std::vector<string> attributionTags = {"tag"};
-
- std::unique_ptr<LogEvent> event8 = CreateAcquireWakelockEvent(
- timestampNs + 7000, attributionUids, attributionTags, "wakelock1");
- std::unique_ptr<LogEvent> event9 = CreateReleaseWakelockEvent(
- timestampNs + 8000, attributionUids, attributionTags, "wakelock2");
-
- mgr.onLogEvent(*event1);
- mgr.onLogEvent(*event2);
- mgr.onLogEvent(*event3);
- mgr.onLogEvent(*event5);
- mgr.onLogEvent(*event5);
- mgr.onLogEvent(*event6);
- mgr.onLogEvent(*event7);
- mgr.onLogEvent(*event8);
- mgr.onLogEvent(*event9);
-
- // Query for UidProcessState of uid 1001
- HashableDimensionKey queryKey1;
- getUidProcessKey(1001, &queryKey1);
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
- getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
-
- // Query for UidProcessState of uid 1004 - not in state map
- HashableDimensionKey queryKey2;
- getUidProcessKey(1004, &queryKey2);
- EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED,
- queryKey2)); // default state
-
- // Query for UidProcessState of uid 1001 - after change in state
- mgr.onLogEvent(*event4);
- EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
- getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
-
- // Query for ScreenState
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
-
- // Query for OverlayState of uid 1000, package name "package2"
- HashableDimensionKey queryKey3;
- getOverlayKey(1000, "package2", &queryKey3);
- EXPECT_EQ(OverlayStateChanged::EXITED,
- getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3));
-
- // Query for WakelockState of uid 1005, tag 2
- HashableDimensionKey queryKey4;
- getPartialWakelockKey(1005, "wakelock2", &queryKey4);
- EXPECT_EQ(WakelockStateChanged::RELEASE,
- getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4));
-
- // Query for WakelockState of uid 1005, tag 1
- HashableDimensionKey queryKey5;
- getPartialWakelockKey(1005, "wakelock1", &queryKey5);
- EXPECT_EQ(WakelockStateChanged::ACQUIRE,
- getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5));
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
deleted file mode 100644
index 1761d5d9e1fa..000000000000
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ /dev/null
@@ -1,1409 +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.
-
-#include "statsd_test_util.h"
-
-#include <aidl/android/util/StatsEventParcel.h>
-
-#include "matchers/SimpleAtomMatchingTracker.h"
-#include "stats_event.h"
-
-using aidl::android::util::StatsEventParcel;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-StatsLogReport outputStreamToProto(ProtoOutputStream* proto) {
- vector<uint8_t> bytes;
- bytes.resize(proto->size());
- size_t pos = 0;
- sp<ProtoReader> reader = proto->data();
-
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- StatsLogReport report;
- report.ParseFromArray(bytes.data(), bytes.size());
- return report;
-}
-
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(atomId);
- return atom_matcher;
-}
-
-AtomMatcher CreateTemperatureAtomMatcher() {
- return CreateSimpleAtomMatcher("TemperatureMatcher", util::TEMPERATURE);
-}
-
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
- ScheduledJobStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::SCHEDULED_JOB_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(3); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateStartScheduledJobAtomMatcher() {
- return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart",
- ScheduledJobStateChanged::STARTED);
-}
-
-AtomMatcher CreateFinishScheduledJobAtomMatcher() {
- return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish",
- ScheduledJobStateChanged::FINISHED);
-}
-
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::SCREEN_BRIGHTNESS_CHANGED);
- return atom_matcher;
-}
-
-AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId("UidProcessStateChanged"));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::UID_PROCESS_STATE_CHANGED);
- return atom_matcher;
-}
-
-AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
- WakelockStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::WAKELOCK_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(4); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateAcquireWakelockAtomMatcher() {
- return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
-}
-
-AtomMatcher CreateReleaseWakelockAtomMatcher() {
- return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
-}
-
-AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher(
- const string& name, BatterySaverModeStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(1); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateBatterySaverModeStartAtomMatcher() {
- return CreateBatterySaverModeStateChangedAtomMatcher(
- "BatterySaverModeStart", BatterySaverModeStateChanged::ON);
-}
-
-
-AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
- return CreateBatterySaverModeStateChangedAtomMatcher(
- "BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
-}
-
-AtomMatcher CreateBatteryStateChangedAtomMatcher(const string& name,
- BatteryPluggedStateEnum state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::PLUGGED_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(1); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateBatteryStateNoneMatcher() {
- return CreateBatteryStateChangedAtomMatcher("BatteryPluggedNone",
- BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
-}
-
-AtomMatcher CreateBatteryStateUsbMatcher() {
- return CreateBatteryStateChangedAtomMatcher("BatteryPluggedUsb",
- BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
-}
-
-AtomMatcher CreateScreenStateChangedAtomMatcher(
- const string& name, android::view::DisplayStateEnum state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::SCREEN_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(1); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateScreenTurnedOnAtomMatcher() {
- return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
- android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-}
-
-AtomMatcher CreateScreenTurnedOffAtomMatcher() {
- return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff",
- ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
-}
-
-AtomMatcher CreateSyncStateChangedAtomMatcher(
- const string& name, SyncStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::SYNC_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(3); // State field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateSyncStartAtomMatcher() {
- return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
-}
-
-AtomMatcher CreateSyncEndAtomMatcher() {
- return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
-}
-
-AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
- const string& name, ActivityForegroundStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(4); // Activity field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateMoveToBackgroundAtomMatcher() {
- return CreateActivityForegroundStateChangedAtomMatcher(
- "Background", ActivityForegroundStateChanged::BACKGROUND);
-}
-
-AtomMatcher CreateMoveToForegroundAtomMatcher() {
- return CreateActivityForegroundStateChangedAtomMatcher(
- "Foreground", ActivityForegroundStateChanged::FOREGROUND);
-}
-
-AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher(
- const string& name, ProcessLifeCycleStateChanged::State state) {
- AtomMatcher atom_matcher;
- atom_matcher.set_id(StringToId(name));
- auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
- field_value_matcher->set_field(3); // Process state field.
- field_value_matcher->set_eq_int(state);
- return atom_matcher;
-}
-
-AtomMatcher CreateProcessCrashAtomMatcher() {
- return CreateProcessLifeCycleStateChangedAtomMatcher(
- "Crashed", ProcessLifeCycleStateChanged::CRASHED);
-}
-
-Predicate CreateScheduledJobPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("ScheduledJobRunningPredicate"));
- predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish"));
- return predicate;
-}
-
-Predicate CreateBatterySaverModePredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("BatterySaverIsOn"));
- predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
- return predicate;
-}
-
-Predicate CreateDeviceUnpluggedPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("DeviceUnplugged"));
- predicate.mutable_simple_predicate()->set_start(StringToId("BatteryPluggedNone"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("BatteryPluggedUsb"));
- return predicate;
-}
-
-Predicate CreateScreenIsOnPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("ScreenIsOn"));
- predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff"));
- return predicate;
-}
-
-Predicate CreateScreenIsOffPredicate() {
- Predicate predicate;
- predicate.set_id(1111123);
- predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
- return predicate;
-}
-
-Predicate CreateHoldingWakelockPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("HoldingWakelock"));
- predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock"));
- return predicate;
-}
-
-Predicate CreateIsSyncingPredicate() {
- Predicate predicate;
- predicate.set_id(33333333333333);
- predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
- return predicate;
-}
-
-Predicate CreateIsInBackgroundPredicate() {
- Predicate predicate;
- predicate.set_id(StringToId("IsInBackground"));
- predicate.mutable_simple_predicate()->set_start(StringToId("Background"));
- predicate.mutable_simple_predicate()->set_stop(StringToId("Foreground"));
- return predicate;
-}
-
-State CreateScreenState() {
- State state;
- state.set_id(StringToId("ScreenState"));
- state.set_atom_id(util::SCREEN_STATE_CHANGED);
- return state;
-}
-
-State CreateUidProcessState() {
- State state;
- state.set_id(StringToId("UidProcessState"));
- state.set_atom_id(util::UID_PROCESS_STATE_CHANGED);
- return state;
-}
-
-State CreateOverlayState() {
- State state;
- state.set_id(StringToId("OverlayState"));
- state.set_atom_id(util::OVERLAY_STATE_CHANGED);
- return state;
-}
-
-State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId) {
- State state;
- state.set_id(StringToId("ScreenStateOnOff"));
- state.set_atom_id(util::SCREEN_STATE_CHANGED);
-
- auto map = CreateScreenStateOnOffMap(screenOnId, screenOffId);
- *state.mutable_map() = map;
-
- return state;
-}
-
-State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) {
- State state;
- state.set_id(StringToId("ScreenStateSimpleOnOff"));
- state.set_atom_id(util::SCREEN_STATE_CHANGED);
-
- auto map = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId);
- *state.mutable_map() = map;
-
- return state;
-}
-
-StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId) {
- StateMap_StateGroup group;
- group.set_group_id(screenOnId);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_VR);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND);
- return group;
-}
-
-StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId) {
- StateMap_StateGroup group;
- group.set_group_id(screenOffId);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND);
- return group;
-}
-
-StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId) {
- StateMap_StateGroup group;
- group.set_group_id(screenOnId);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- return group;
-}
-
-StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId) {
- StateMap_StateGroup group;
- group.set_group_id(screenOffId);
- group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- return group;
-}
-
-StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId) {
- StateMap map;
- *map.add_group() = CreateScreenStateOnGroup(screenOnId);
- *map.add_group() = CreateScreenStateOffGroup(screenOffId);
- return map;
-}
-
-StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) {
- StateMap map;
- *map.add_group() = CreateScreenStateSimpleOnGroup(screenOnId);
- *map.add_group() = CreateScreenStateSimpleOffGroup(screenOffId);
- return map;
-}
-
-void addPredicateToPredicateCombination(const Predicate& predicate,
- Predicate* combinationPredicate) {
- combinationPredicate->mutable_combination()->add_predicate(predicate.id());
-}
-
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
- const std::vector<Position>& positions) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const auto position : positions) {
- auto child = dimensions.add_child();
- child->set_field(1);
- child->set_position(position);
- child->add_child()->set_field(1);
- }
- return dimensions;
-}
-
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
- const std::vector<Position>& positions) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const auto position : positions) {
- auto child = dimensions.add_child();
- child->set_field(1);
- child->set_position(position);
- child->add_child()->set_field(1);
- child->add_child()->set_field(2);
- }
- return dimensions;
-}
-
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
- FieldMatcher dimensions;
- dimensions.set_field(atomId);
- for (const int field : fields) {
- dimensions.add_child()->set_field(field);
- }
- return dimensions;
-}
-
-FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId,
- const std::vector<Position>& positions,
- const std::vector<int>& fields) {
- FieldMatcher dimensions = CreateAttributionUidDimensions(atomId, positions);
-
- for (const int field : fields) {
- dimensions.add_child()->set_field(field);
- }
- return dimensions;
-}
-
-// START: get primary key functions
-void getUidProcessKey(int uid, HashableDimensionKey* key) {
- int pos1[] = {1, 0, 0};
- Field field1(27 /* atom id */, pos1, 0 /* depth */);
- Value value1((int32_t)uid);
-
- key->addValue(FieldValue(field1, value1));
-}
-
-void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
- int pos1[] = {1, 0, 0};
- int pos2[] = {2, 0, 0};
-
- Field field1(59 /* atom id */, pos1, 0 /* depth */);
- Field field2(59 /* atom id */, pos2, 0 /* depth */);
-
- Value value1((int32_t)uid);
- Value value2(packageName);
-
- key->addValue(FieldValue(field1, value1));
- key->addValue(FieldValue(field2, value2));
-}
-
-void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) {
- int pos1[] = {1, 1, 1};
- int pos3[] = {2, 0, 0};
- int pos4[] = {3, 0, 0};
-
- Field field1(10 /* atom id */, pos1, 2 /* depth */);
-
- Field field3(10 /* atom id */, pos3, 0 /* depth */);
- Field field4(10 /* atom id */, pos4, 0 /* depth */);
-
- Value value1((int32_t)uid);
- Value value3((int32_t)1 /*partial*/);
- Value value4(tag);
-
- key->addValue(FieldValue(field1, value1));
- key->addValue(FieldValue(field3, value3));
- key->addValue(FieldValue(field4, value4));
-}
-
-void getPartialWakelockKey(int uid, HashableDimensionKey* key) {
- int pos1[] = {1, 1, 1};
- int pos3[] = {2, 0, 0};
-
- Field field1(10 /* atom id */, pos1, 2 /* depth */);
- Field field3(10 /* atom id */, pos3, 0 /* depth */);
-
- Value value1((int32_t)uid);
- Value value3((int32_t)1 /*partial*/);
-
- key->addValue(FieldValue(field1, value1));
- key->addValue(FieldValue(field3, value3));
-}
-// END: get primary key functions
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
- const vector<string>& attributionTags) {
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
-}
-
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
-}
-
-void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, value1);
- AStatsEvent_writeInt32(statsEvent, value2);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2) {
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2);
- return logEvent;
-}
-
-void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2, int32_t value3) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, value1);
- AStatsEvent_writeInt32(statsEvent, value2);
- AStatsEvent_writeInt32(statsEvent, value3);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2, int32_t value3) {
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- CreateThreeValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2, value3);
- return logEvent;
-}
-
-void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
- int32_t value) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_writeInt32(statsEvent, value);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- CreateRepeatedValueLogEvent(logEvent.get(), atomId, eventTimeNs, value);
- return logEvent;
-}
-
-void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- parseStatsEventToLogEvent(statsEvent, logEvent);
-}
-
-shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- CreateNoValuesLogEvent(logEvent.get(), atomId, eventTimeNs);
- return logEvent;
-}
-
-shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1,
- int data2) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
- AStatsEvent_writeInt32(statsEvent, data1);
- AStatsEvent_writeInt32(statsEvent, data2);
-
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs,
- const vector<int>& uids, const vector<string>& tags,
- int data1, int data2) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- writeAttribution(statsEvent, uids, tags);
- AStatsEvent_writeInt32(statsEvent, data1);
- AStatsEvent_writeInt32(statsEvent, data2);
-
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids) {
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(ReturnArg<0>());
- for (const int isolatedUid : isolatedUids) {
- EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
- }
-
- return uidMap;
-}
-
-sp<MockUidMap> makeMockUidMapForPackage(const string& pkg, const set<int32_t>& uids) {
- sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getAppUid(_)).Times(AnyNumber());
- EXPECT_CALL(*uidMap, getAppUid(pkg)).WillRepeatedly(Return(uids));
-
- return uidMap;
-}
-
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(uint64_t timestampNs,
- const android::view::DisplayStateEnum state,
- int loggerUid) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(loggerUid, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_writeInt32(statsEvent, level);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
- const vector<int>& attributionUids, const vector<string>& attributionTags,
- const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, jobName.c_str());
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName) {
- return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
- ScheduledJobStateChanged::STARTED, timestampNs);
-}
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName) {
- return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
- ScheduledJobStateChanged::FINISHED, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& wakelockName,
- const WakelockStateChanged::State state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::WAKELOCK_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
- AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
- AStatsEvent_writeString(statsEvent, wakelockName.c_str());
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
- AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& wakelockName) {
- return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags,
- wakelockName, WakelockStateChanged::ACQUIRE);
-}
-
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& wakelockName) {
- return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags,
- wakelockName, WakelockStateChanged::RELEASE);
-}
-
-std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
- uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeString(statsEvent, "pkg_name");
- AStatsEvent_writeString(statsEvent, "class_name");
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) {
- return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
- ActivityForegroundStateChanged::BACKGROUND);
-}
-
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) {
- return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
- ActivityForegroundStateChanged::FOREGROUND);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name,
- const SyncStateChanged::State state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name) {
- return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
- SyncStateChanged::ON);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& name) {
- return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
- SyncStateChanged::OFF);
-}
-
-std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
- uint64_t timestampNs, const int uid, const ProcessLifeCycleStateChanged::State state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeString(statsEvent, "");
- AStatsEvent_writeInt32(statsEvent, state);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid) {
- return CreateProcessLifeCycleStateChangedEvent(timestampNs, uid,
- ProcessLifeCycleStateChanged::CRASHED);
-}
-
-std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::APP_CRASH_OCCURRED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_writeString(statsEvent, "eventType");
- AStatsEvent_writeString(statsEvent, "processName");
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid,
- int isolatedUid, bool is_create) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::ISOLATED_UID_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, hostUid);
- AStatsEvent_writeInt32(statsEvent, isolatedUid);
- AStatsEvent_writeInt32(statsEvent, is_create);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
- uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::UID_PROCESS_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
- AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const BleScanStateChanged::State state,
- const bool filtered, const bool firstMatch,
- const bool opportunistic) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- writeAttribution(statsEvent, attributionUids, attributionTags);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
- AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
- if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) {
- AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET,
- util::BLE_SCAN_STATE_CHANGED__STATE__OFF);
- }
- AStatsEvent_writeBool(statsEvent, filtered); // filtered
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
- AStatsEvent_writeBool(statsEvent, firstMatch); // first match
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
- AStatsEvent_writeBool(statsEvent, opportunistic); // opportunistic
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
- const string& packageName,
- const bool usingAlertWindow,
- const OverlayStateChanged::State state) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
- AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
- AStatsEvent_writeInt32(statsEvent, uid);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
- AStatsEvent_writeString(statsEvent, packageName.c_str());
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
- AStatsEvent_writeBool(statsEvent, usingAlertWindow);
- AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
- AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, false);
-
- std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- parseStatsEventToLogEvent(statsEvent, logEvent.get());
- return logEvent;
-}
-
-sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
- const StatsdConfig& config, const ConfigKey& key,
- const shared_ptr<IPullAtomCallback>& puller,
- const int32_t atomTag, const sp<UidMap> uidMap) {
- sp<StatsPullerManager> pullerManager = new StatsPullerManager();
- if (puller != nullptr) {
- pullerManager->RegisterPullAtomCallback(/*uid=*/0, atomTag, NS_PER_SEC, NS_PER_SEC * 10, {},
- puller);
- }
- sp<AlarmMonitor> anomalyAlarmMonitor =
- new AlarmMonitor(1,
- [](const shared_ptr<IStatsCompanionService>&, int64_t){},
- [](const shared_ptr<IStatsCompanionService>&){});
- sp<AlarmMonitor> periodicAlarmMonitor =
- new AlarmMonitor(1,
- [](const shared_ptr<IStatsCompanionService>&, int64_t){},
- [](const shared_ptr<IStatsCompanionService>&){});
- sp<StatsLogProcessor> processor =
- new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
- timeBaseNs, [](const ConfigKey&) { return true; },
- [](const int&, const vector<int64_t>&) {return true;});
- processor->OnConfigUpdated(currentTimeNs, key, config);
- return processor;
-}
-
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
- std::sort(events->begin(), events->end(),
- [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
- return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs();
- });
-}
-
-int64_t StringToId(const string& str) {
- return static_cast<int64_t>(std::hash<std::string>()(str));
-}
-
-sp<EventMatcherWizard> createEventMatcherWizard(
- int tagId, int matcherIndex, const vector<FieldValueMatcher>& fieldValueMatchers) {
- sp<UidMap> uidMap = new UidMap();
- SimpleAtomMatcher atomMatcher;
- atomMatcher.set_atom_id(tagId);
- for (const FieldValueMatcher& fvm : fieldValueMatchers) {
- *atomMatcher.add_field_value_matcher() = fvm;
- }
- uint64_t matcherHash = 0x12345678;
- int64_t matcherId = 678;
- return new EventMatcherWizard({new SimpleAtomMatchingTracker(
- matcherId, matcherIndex, matcherHash, atomMatcher, uidMap)});
-}
-
-void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
- const int uid, const string& tag) {
- EXPECT_EQ(value.field(), atomId);
- ASSERT_EQ(value.value_tuple().dimensions_value_size(), 2);
- // Attribution field.
- EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
- // Uid field.
- ASSERT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
- uid);
- // Tag field.
- EXPECT_EQ(value.value_tuple().dimensions_value(1).field(), 3);
- EXPECT_EQ(value.value_tuple().dimensions_value(1).value_str(), tag);
-}
-
-void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
- EXPECT_EQ(value.field(), atomId);
- ASSERT_EQ(value.value_tuple().dimensions_value_size(), 1);
- // Attribution field.
- EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
- // Uid only.
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), uid);
-}
-
-void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid) {
- EXPECT_EQ(value.field(), atomId);
- ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx);
- // Attribution field.
- EXPECT_EQ(value.value_tuple().dimensions_value(node_idx).field(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(node_idx)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(node_idx)
- .value_tuple().dimensions_value(0).value_int(), uid);
-}
-
-void ValidateAttributionUidAndTagDimension(
- const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag) {
- EXPECT_EQ(value.field(), atomId);
- ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx);
- // Attribution field.
- EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx).field());
- // Uid only.
- EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx)
- .value_tuple().dimensions_value_size());
- EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx)
- .value_tuple().dimensions_value(0).field());
- EXPECT_EQ(uid, value.value_tuple().dimensions_value(node_idx)
- .value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx)
- .value_tuple().dimensions_value(1).field());
- EXPECT_EQ(tag, value.value_tuple().dimensions_value(node_idx)
- .value_tuple().dimensions_value(1).value_str());
-}
-
-void ValidateAttributionUidAndTagDimension(
- const DimensionsValue& value, int atomId, int uid, const std::string& tag) {
- EXPECT_EQ(value.field(), atomId);
- ASSERT_EQ(1, value.value_tuple().dimensions_value_size());
- // Attribution field.
- EXPECT_EQ(1, value.value_tuple().dimensions_value(0).field());
- // Uid only.
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 2);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), uid);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(1).field(), 2);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(1).value_str(), tag);
-}
-
-bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
- if (s1.field() != s2.field()) {
- return false;
- }
- if (s1.value_case() != s2.value_case()) {
- return false;
- }
- switch (s1.value_case()) {
- case DimensionsValue::ValueCase::kValueStr:
- return (s1.value_str() == s2.value_str());
- case DimensionsValue::ValueCase::kValueInt:
- return s1.value_int() == s2.value_int();
- case DimensionsValue::ValueCase::kValueLong:
- return s1.value_long() == s2.value_long();
- case DimensionsValue::ValueCase::kValueBool:
- return s1.value_bool() == s2.value_bool();
- case DimensionsValue::ValueCase::kValueFloat:
- return s1.value_float() == s2.value_float();
- case DimensionsValue::ValueCase::kValueTuple: {
- if (s1.value_tuple().dimensions_value_size() !=
- s2.value_tuple().dimensions_value_size()) {
- return false;
- }
- bool allMatched = true;
- for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) {
- allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i),
- s2.value_tuple().dimensions_value(i));
- }
- return allMatched;
- }
- case DimensionsValue::ValueCase::VALUE_NOT_SET:
- default:
- return true;
- }
-}
-
-bool LessThan(const google::protobuf::RepeatedPtrField<StateValue>& s1,
- const google::protobuf::RepeatedPtrField<StateValue>& s2) {
- if (s1.size() != s2.size()) {
- return s1.size() < s2.size();
- }
- for (int i = 0; i < s1.size(); i++) {
- const StateValue& state1 = s1[i];
- const StateValue& state2 = s2[i];
- if (state1.atom_id() != state2.atom_id()) {
- return state1.atom_id() < state2.atom_id();
- }
- if (state1.value() != state2.value()) {
- return state1.value() < state2.value();
- }
- if (state1.group_id() != state2.group_id()) {
- return state1.group_id() < state2.group_id();
- }
- }
- return false;
-}
-
-bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) {
- if (s1.field() != s2.field()) {
- return s1.field() < s2.field();
- }
- if (s1.value_case() != s2.value_case()) {
- return s1.value_case() < s2.value_case();
- }
- switch (s1.value_case()) {
- case DimensionsValue::ValueCase::kValueStr:
- return s1.value_str() < s2.value_str();
- case DimensionsValue::ValueCase::kValueInt:
- return s1.value_int() < s2.value_int();
- case DimensionsValue::ValueCase::kValueLong:
- return s1.value_long() < s2.value_long();
- case DimensionsValue::ValueCase::kValueBool:
- return (int)s1.value_bool() < (int)s2.value_bool();
- case DimensionsValue::ValueCase::kValueFloat:
- return s1.value_float() < s2.value_float();
- case DimensionsValue::ValueCase::kValueTuple: {
- if (s1.value_tuple().dimensions_value_size() !=
- s2.value_tuple().dimensions_value_size()) {
- return s1.value_tuple().dimensions_value_size() <
- s2.value_tuple().dimensions_value_size();
- }
- for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) {
- if (EqualsTo(s1.value_tuple().dimensions_value(i),
- s2.value_tuple().dimensions_value(i))) {
- continue;
- } else {
- return LessThan(s1.value_tuple().dimensions_value(i),
- s2.value_tuple().dimensions_value(i));
- }
- }
- return false;
- }
- case DimensionsValue::ValueCase::VALUE_NOT_SET:
- default:
- return false;
- }
-}
-
-bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) {
- if (LessThan(s1.dimInWhat, s2.dimInWhat)) {
- return true;
- } else if (LessThan(s2.dimInWhat, s1.dimInWhat)) {
- return false;
- }
-
- return LessThan(s1.stateValues, s2.stateValues);
-}
-
-void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
- DimensionsValue* dimension) {
- if (dimension->has_value_str_hash()) {
- auto it = str_map.find((uint64_t)(dimension->value_str_hash()));
- if (it != str_map.end()) {
- dimension->clear_value_str_hash();
- dimension->set_value_str(it->second);
- } else {
- ALOGE("Can not find the string hash: %llu",
- (unsigned long long)dimension->value_str_hash());
- }
- } else if (dimension->has_value_tuple()) {
- auto value_tuple = dimension->mutable_value_tuple();
- for (int i = 0; i < value_tuple->dimensions_value_size(); ++i) {
- backfillStringInDimension(str_map, value_tuple->mutable_dimensions_value(i));
- }
- }
-}
-
-void backfillStringInReport(ConfigMetricsReport *config_report) {
- std::map<uint64_t, string> str_map;
- for (const auto& str : config_report->strings()) {
- uint64_t hash = Hash64(str);
- if (str_map.find(hash) != str_map.end()) {
- ALOGE("String hash conflicts: %s %s", str.c_str(), str_map[hash].c_str());
- }
- str_map[hash] = str;
- }
- for (int i = 0; i < config_report->metrics_size(); ++i) {
- auto metric_report = config_report->mutable_metrics(i);
- if (metric_report->has_count_metrics()) {
- backfillStringInDimension(str_map, metric_report->mutable_count_metrics());
- } else if (metric_report->has_duration_metrics()) {
- backfillStringInDimension(str_map, metric_report->mutable_duration_metrics());
- } else if (metric_report->has_gauge_metrics()) {
- backfillStringInDimension(str_map, metric_report->mutable_gauge_metrics());
- } else if (metric_report->has_value_metrics()) {
- backfillStringInDimension(str_map, metric_report->mutable_value_metrics());
- }
- }
- // Backfill the package names.
- for (int i = 0 ; i < config_report->uid_map().snapshots_size(); ++i) {
- auto snapshot = config_report->mutable_uid_map()->mutable_snapshots(i);
- for (int j = 0 ; j < snapshot->package_info_size(); ++j) {
- auto package_info = snapshot->mutable_package_info(j);
- if (package_info->has_name_hash()) {
- auto it = str_map.find((uint64_t)(package_info->name_hash()));
- if (it != str_map.end()) {
- package_info->clear_name_hash();
- package_info->set_name(it->second);
- } else {
- ALOGE("Can not find the string package name hash: %llu",
- (unsigned long long)package_info->name_hash());
- }
-
- }
- }
- }
- // Backfill the app name in app changes.
- for (int i = 0 ; i < config_report->uid_map().changes_size(); ++i) {
- auto change = config_report->mutable_uid_map()->mutable_changes(i);
- if (change->has_app_hash()) {
- auto it = str_map.find((uint64_t)(change->app_hash()));
- if (it != str_map.end()) {
- change->clear_app_hash();
- change->set_app(it->second);
- } else {
- ALOGE("Can not find the string change app name hash: %llu",
- (unsigned long long)change->app_hash());
- }
- }
- }
-}
-
-void backfillStringInReport(ConfigMetricsReportList *config_report_list) {
- for (int i = 0; i < config_report_list->reports_size(); ++i) {
- backfillStringInReport(config_report_list->mutable_reports(i));
- }
-}
-
-bool backfillDimensionPath(const DimensionsValue& path,
- const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
- int* leafIndex,
- DimensionsValue* dimension) {
- dimension->set_field(path.field());
- if (path.has_value_tuple()) {
- for (int i = 0; i < path.value_tuple().dimensions_value_size(); ++i) {
- if (!backfillDimensionPath(
- path.value_tuple().dimensions_value(i), leafValues, leafIndex,
- dimension->mutable_value_tuple()->add_dimensions_value())) {
- return false;
- }
- }
- } else {
- if (*leafIndex < 0 || *leafIndex >= leafValues.size()) {
- return false;
- }
- dimension->MergeFrom(leafValues.Get(*leafIndex));
- (*leafIndex)++;
- }
- return true;
-}
-
-bool backfillDimensionPath(const DimensionsValue& path,
- const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
- DimensionsValue* dimension) {
- int leafIndex = 0;
- return backfillDimensionPath(path, leafValues, &leafIndex, dimension);
-}
-
-void backfillDimensionPath(ConfigMetricsReportList *config_report_list) {
- for (int i = 0; i < config_report_list->reports_size(); ++i) {
- auto report = config_report_list->mutable_reports(i);
- for (int j = 0; j < report->metrics_size(); ++j) {
- auto metric_report = report->mutable_metrics(j);
- if (metric_report->has_dimensions_path_in_what() ||
- metric_report->has_dimensions_path_in_condition()) {
- auto whatPath = metric_report->dimensions_path_in_what();
- auto conditionPath = metric_report->dimensions_path_in_condition();
- if (metric_report->has_count_metrics()) {
- backfillDimensionPath(whatPath, conditionPath,
- metric_report->mutable_count_metrics());
- } else if (metric_report->has_duration_metrics()) {
- backfillDimensionPath(whatPath, conditionPath,
- metric_report->mutable_duration_metrics());
- } else if (metric_report->has_gauge_metrics()) {
- backfillDimensionPath(whatPath, conditionPath,
- metric_report->mutable_gauge_metrics());
- } else if (metric_report->has_value_metrics()) {
- backfillDimensionPath(whatPath, conditionPath,
- metric_report->mutable_value_metrics());
- }
- metric_report->clear_dimensions_path_in_what();
- metric_report->clear_dimensions_path_in_condition();
- }
- }
- }
-}
-
-void backfillStartEndTimestamp(StatsLogReport *report) {
- const int64_t timeBaseNs = report->time_base_elapsed_nano_seconds();
- const int64_t bucketSizeNs = report->bucket_size_nano_seconds();
- if (report->has_count_metrics()) {
- backfillStartEndTimestampForMetrics(
- timeBaseNs, bucketSizeNs, report->mutable_count_metrics());
- } else if (report->has_duration_metrics()) {
- backfillStartEndTimestampForMetrics(
- timeBaseNs, bucketSizeNs, report->mutable_duration_metrics());
- } else if (report->has_gauge_metrics()) {
- backfillStartEndTimestampForMetrics(
- timeBaseNs, bucketSizeNs, report->mutable_gauge_metrics());
- if (report->gauge_metrics().skipped_size() > 0) {
- backfillStartEndTimestampForSkippedBuckets(
- timeBaseNs, report->mutable_gauge_metrics());
- }
- } else if (report->has_value_metrics()) {
- backfillStartEndTimestampForMetrics(
- timeBaseNs, bucketSizeNs, report->mutable_value_metrics());
- if (report->value_metrics().skipped_size() > 0) {
- backfillStartEndTimestampForSkippedBuckets(
- timeBaseNs, report->mutable_value_metrics());
- }
- }
-}
-
-void backfillStartEndTimestamp(ConfigMetricsReport *config_report) {
- for (int j = 0; j < config_report->metrics_size(); ++j) {
- backfillStartEndTimestamp(config_report->mutable_metrics(j));
- }
-}
-
-void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) {
- for (int i = 0; i < config_report_list->reports_size(); ++i) {
- backfillStartEndTimestamp(config_report_list->mutable_reports(i));
- }
-}
-
-Status FakeSubsystemSleepCallback::onPullAtom(int atomTag,
- const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
- // Convert stats_events into StatsEventParcels.
- std::vector<StatsEventParcel> parcels;
- for (int i = 1; i < 3; i++) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomTag);
- std::string subsystemName = "subsystem_name_";
- subsystemName = subsystemName + std::to_string(i);
- AStatsEvent_writeString(event, subsystemName.c_str());
- AStatsEvent_writeString(event, "subsystem_subname foo");
- AStatsEvent_writeInt64(event, /*count= */ i);
- AStatsEvent_writeInt64(event, /*time_millis= */ i * 100);
- AStatsEvent_build(event);
- size_t size;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
-
- StatsEventParcel p;
- // vector.assign() creates a copy, but this is inevitable unless
- // stats_event.h/c uses a vector as opposed to a buffer.
- p.buffer.assign(buffer, buffer + size);
- parcels.push_back(std::move(p));
- AStatsEvent_release(event);
- }
- resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
- return Status::ok();
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
deleted file mode 100644
index 1220019e2353..000000000000
--- a/cmds/statsd/tests/statsd_test_util.h
+++ /dev/null
@@ -1,483 +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.
-
-#pragma once
-
-#include <aidl/android/os/BnPullAtomCallback.h>
-#include <aidl/android/os/IPullAtomCallback.h>
-#include <aidl/android/os/IPullAtomResultReceiver.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "src/StatsLogProcessor.h"
-#include "src/hash.h"
-#include "src/logd/LogEvent.h"
-#include "src/matchers/EventMatcherWizard.h"
-#include "src/packages/UidMap.h"
-#include "src/stats_log_util.h"
-#include "stats_event.h"
-#include "statslog_statsdtest.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using namespace testing;
-using ::aidl::android::os::BnPullAtomCallback;
-using ::aidl::android::os::IPullAtomCallback;
-using ::aidl::android::os::IPullAtomResultReceiver;
-using android::util::ProtoReader;
-using google::protobuf::RepeatedPtrField;
-using Status = ::ndk::ScopedAStatus;
-
-const int SCREEN_STATE_ATOM_ID = util::SCREEN_STATE_CHANGED;
-const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED;
-
-enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE };
-
-class MockUidMap : public UidMap {
-public:
- MOCK_METHOD(int, getHostUidOrSelf, (int uid), (const));
- MOCK_METHOD(std::set<int32_t>, getAppUid, (const string& package), (const));
-};
-
-// Converts a ProtoOutputStream to a StatsLogReport proto.
-StatsLogReport outputStreamToProto(ProtoOutputStream* proto);
-
-// Create AtomMatcher proto to simply match a specific atom type.
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
-
-// Create AtomMatcher proto for temperature atom.
-AtomMatcher CreateTemperatureAtomMatcher();
-
-// Create AtomMatcher proto for scheduled job state changed.
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
-
-// Create AtomMatcher proto for starting a scheduled job.
-AtomMatcher CreateStartScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for a scheduled job is done.
-AtomMatcher CreateFinishScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for screen brightness state changed.
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
-
-// Create AtomMatcher proto for starting battery save mode.
-AtomMatcher CreateBatterySaverModeStartAtomMatcher();
-
-// Create AtomMatcher proto for stopping battery save mode.
-AtomMatcher CreateBatterySaverModeStopAtomMatcher();
-
-// Create AtomMatcher proto for battery state none mode.
-AtomMatcher CreateBatteryStateNoneMatcher();
-
-// Create AtomMatcher proto for battery state usb mode.
-AtomMatcher CreateBatteryStateUsbMatcher();
-
-// Create AtomMatcher proto for process state changed.
-AtomMatcher CreateUidProcessStateChangedAtomMatcher();
-
-// Create AtomMatcher proto for acquiring wakelock.
-AtomMatcher CreateAcquireWakelockAtomMatcher();
-
-// Create AtomMatcher proto for releasing wakelock.
-AtomMatcher CreateReleaseWakelockAtomMatcher() ;
-
-// Create AtomMatcher proto for screen turned on.
-AtomMatcher CreateScreenTurnedOnAtomMatcher();
-
-// Create AtomMatcher proto for screen turned off.
-AtomMatcher CreateScreenTurnedOffAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned on.
-AtomMatcher CreateSyncStartAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned off.
-AtomMatcher CreateSyncEndAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to background.
-AtomMatcher CreateMoveToBackgroundAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to foreground.
-AtomMatcher CreateMoveToForegroundAtomMatcher();
-
-// Create AtomMatcher proto for process crashes
-AtomMatcher CreateProcessCrashAtomMatcher() ;
-
-// Create Predicate proto for screen is on.
-Predicate CreateScreenIsOnPredicate();
-
-// Create Predicate proto for screen is off.
-Predicate CreateScreenIsOffPredicate();
-
-// Create Predicate proto for a running scheduled job.
-Predicate CreateScheduledJobPredicate();
-
-// Create Predicate proto for battery saver mode.
-Predicate CreateBatterySaverModePredicate();
-
-// Create Predicate proto for device unplogged mode.
-Predicate CreateDeviceUnpluggedPredicate();
-
-// Create Predicate proto for holding wakelock.
-Predicate CreateHoldingWakelockPredicate();
-
-// Create a Predicate proto for app syncing.
-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();
-
-// Create State proto for screen state atom with on/off map.
-State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId);
-
-// Create State proto for screen state atom with simple on/off map.
-State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId);
-
-// Create StateGroup proto for ScreenState ON group
-StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId);
-
-// Create StateGroup proto for ScreenState OFF group
-StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId);
-
-// Create StateGroup proto for simple ScreenState ON group
-StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId);
-
-// Create StateGroup proto for simple ScreenState OFF group
-StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId);
-
-// Create StateMap proto for ScreenState ON/OFF map
-StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId);
-
-// Create StateMap proto for simple ScreenState ON/OFF map
-StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId);
-
-// Add a predicate to the predicate combination.
-void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
-
-// Create dimensions from primitive fields.
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields);
-
-// Create dimensions by attribution uid and tag.
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
- const std::vector<Position>& positions);
-
-// Create dimensions by attribution uid only.
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
- const std::vector<Position>& positions);
-
-FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId,
- const std::vector<Position>& positions,
- const std::vector<int>& fields);
-
-// START: get primary key functions
-// These functions take in atom field information and create FieldValues which are stored in the
-// given HashableDimensionKey.
-void getUidProcessKey(int uid, HashableDimensionKey* key);
-
-void getOverlayKey(int uid, string packageName, HashableDimensionKey* key);
-
-void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key);
-
-void getPartialWakelockKey(int uid, HashableDimensionKey* key);
-// END: get primary key functions
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
- const vector<string>& attributionTags);
-
-// Builds statsEvent to get buffer that is parsed into logEvent then releases statsEvent.
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
-
-shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2);
-
-void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2);
-
-shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2, int32_t value3);
-
-void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2, int32_t value3);
-
-// The repeated value log event helpers create a log event with two int fields, both
-// set to the same value. This is useful for testing metrics that are only interested
-// in the value of the second field but still need the first field to be populated.
-std::shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs,
- int32_t value);
-
-void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
- int32_t value);
-
-std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs);
-
-void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs);
-
-std::shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1,
- int data2);
-
-std::shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs,
- const vector<int>& uids,
- const vector<string>& tags, int data1, int data2);
-
-sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids);
-
-sp<MockUidMap> makeMockUidMapForPackage(const string& pkg, const set<int32_t>& uids);
-
-// Create log event for screen state changed.
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(uint64_t timestampNs,
- const android::view::DisplayStateEnum state,
- int loggerUid = 0);
-
-// Create log event for screen brightness state changed.
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level);
-
-// Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName);
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const string& jobName);
-
-// Create log event when battery saver starts.
-std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
-// Create log event when battery saver stops.
-std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
-
-// Create log event when battery state changes.
-std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state);
-
-// Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid);
-
-// Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid);
-
-// Create log event when the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, const vector<int>& uids,
- const vector<string>& tags, const string& name);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, const vector<int>& uids,
- const vector<string>& tags, const string& name);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid);
-
-// Create log event for an app crash.
-std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid);
-
-// Create log event for acquiring wakelock.
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
- const vector<string>& tags,
- const string& wakelockName);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
- const vector<string>& tags,
- const string& wakelockName);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid,
- int isolatedUid, bool is_create);
-
-// Create log event for uid process state change.
-std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
- uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state);
-
-std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
- const vector<int>& attributionUids,
- const vector<string>& attributionTags,
- const BleScanStateChanged::State state,
- const bool filtered, const bool firstMatch,
- const bool opportunistic);
-
-std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
- const string& packageName,
- const bool usingAlertWindow,
- const OverlayStateChanged::State state);
-
-// Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
- const StatsdConfig& config, const ConfigKey& key,
- const shared_ptr<IPullAtomCallback>& puller = nullptr,
- const int32_t atomTag = 0 /*for puller only*/,
- const sp<UidMap> = new UidMap());
-
-// Util function to sort the log events by timestamp.
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
-
-int64_t StringToId(const string& str);
-
-sp<EventMatcherWizard> createEventMatcherWizard(
- int tagId, int matcherIndex, const std::vector<FieldValueMatcher>& fieldValueMatchers = {});
-
-void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
- const int uid, const string& tag);
-void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
-void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid);
-void ValidateAttributionUidAndTagDimension(
- const DimensionsValue& value, int atomId, int uid, const std::string& tag);
-void ValidateAttributionUidAndTagDimension(
- const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag);
-
-struct DimensionsPair {
- DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2)
- : dimInWhat(m1), stateValues(m2){};
-
- DimensionsValue dimInWhat;
- google::protobuf::RepeatedPtrField<StateValue> stateValues;
-};
-
-bool LessThan(const StateValue& s1, const StateValue& s2);
-bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2);
-bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2);
-
-
-void backfillStartEndTimestamp(ConfigMetricsReport *config_report);
-void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list);
-
-void backfillStringInReport(ConfigMetricsReportList *config_report_list);
-void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
- DimensionsValue* dimension);
-
-template <typename T>
-void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
- T* metrics) {
- for (int i = 0; i < metrics->data_size(); ++i) {
- auto data = metrics->mutable_data(i);
- if (data->has_dimensions_in_what()) {
- backfillStringInDimension(str_map, data->mutable_dimensions_in_what());
- }
- if (data->has_dimensions_in_condition()) {
- backfillStringInDimension(str_map, data->mutable_dimensions_in_condition());
- }
- }
-}
-
-void backfillDimensionPath(ConfigMetricsReportList* config_report_list);
-
-bool backfillDimensionPath(const DimensionsValue& path,
- const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
- DimensionsValue* dimension);
-
-class FakeSubsystemSleepCallback : public BnPullAtomCallback {
-public:
- Status onPullAtom(int atomTag,
- const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override;
-};
-
-template <typename T>
-void backfillDimensionPath(const DimensionsValue& whatPath,
- const DimensionsValue& conditionPath,
- T* metricData) {
- for (int i = 0; i < metricData->data_size(); ++i) {
- auto data = metricData->mutable_data(i);
- if (data->dimension_leaf_values_in_what_size() > 0) {
- backfillDimensionPath(whatPath, data->dimension_leaf_values_in_what(),
- data->mutable_dimensions_in_what());
- data->clear_dimension_leaf_values_in_what();
- }
- if (data->dimension_leaf_values_in_condition_size() > 0) {
- backfillDimensionPath(conditionPath, data->dimension_leaf_values_in_condition(),
- data->mutable_dimensions_in_condition());
- data->clear_dimension_leaf_values_in_condition();
- }
- }
-}
-
-struct DimensionCompare {
- bool operator()(const DimensionsPair& s1, const DimensionsPair& s2) const {
- return LessThan(s1, s2);
- }
-};
-
-template <typename T>
-void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
- std::map<DimensionsPair, int, DimensionCompare> dimensionIndexMap;
- for (int i = 0; i < metricData.data_size(); ++i) {
- dimensionIndexMap.insert(
- std::make_pair(DimensionsPair(metricData.data(i).dimensions_in_what(),
- metricData.data(i).slice_by_state()),
- i));
- }
- for (const auto& itr : dimensionIndexMap) {
- *sortedMetricData->add_data() = metricData.data(itr.second);
- }
-}
-
-template <typename T>
-void backfillStartEndTimestampForFullBucket(
- const int64_t timeBaseNs, const int64_t bucketSizeNs, T* bucket) {
- bucket->set_start_bucket_elapsed_nanos(timeBaseNs + bucketSizeNs * bucket->bucket_num());
- bucket->set_end_bucket_elapsed_nanos(
- timeBaseNs + bucketSizeNs * bucket->bucket_num() + bucketSizeNs);
- bucket->clear_bucket_num();
-}
-
-template <typename T>
-void backfillStartEndTimestampForPartialBucket(const int64_t timeBaseNs, T* bucket) {
- if (bucket->has_start_bucket_elapsed_millis()) {
- bucket->set_start_bucket_elapsed_nanos(
- MillisToNano(bucket->start_bucket_elapsed_millis()));
- bucket->clear_start_bucket_elapsed_millis();
- }
- if (bucket->has_end_bucket_elapsed_millis()) {
- bucket->set_end_bucket_elapsed_nanos(
- MillisToNano(bucket->end_bucket_elapsed_millis()));
- bucket->clear_end_bucket_elapsed_millis();
- }
-}
-
-template <typename T>
-void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, const int64_t bucketSizeNs,
- T* metrics) {
- for (int i = 0; i < metrics->data_size(); ++i) {
- auto data = metrics->mutable_data(i);
- for (int j = 0; j < data->bucket_info_size(); ++j) {
- auto bucket = data->mutable_bucket_info(j);
- if (bucket->has_bucket_num()) {
- backfillStartEndTimestampForFullBucket(timeBaseNs, bucketSizeNs, bucket);
- } else {
- backfillStartEndTimestampForPartialBucket(timeBaseNs, bucket);
- }
- }
- }
-}
-
-template <typename T>
-void backfillStartEndTimestampForSkippedBuckets(const int64_t timeBaseNs, T* metrics) {
- for (int i = 0; i < metrics->skipped_size(); ++i) {
- backfillStartEndTimestampForPartialBucket(timeBaseNs, metrics->mutable_skipped(i));
- }
-}
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
deleted file mode 100644
index 74eafbf56d66..000000000000
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-// 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 <android-base/unique_fd.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-#include "src/storage/StorageManager.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using namespace testing;
-using std::make_shared;
-using std::shared_ptr;
-using std::vector;
-using testing::Contains;
-
-TEST(StorageManagerTest, TrainInfoReadWriteTest) {
- InstallTrainInfo trainInfo;
- trainInfo.trainVersionCode = 12345;
- trainInfo.trainName = "This is a train name #)$(&&$";
- trainInfo.status = 1;
- const char* expIds = "test_ids";
- trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
-
- bool result;
-
- result = StorageManager::writeTrainInfo(trainInfo);
-
- EXPECT_TRUE(result);
-
- InstallTrainInfo trainInfoResult;
- result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
- EXPECT_TRUE(result);
-
- EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
- ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
- EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
- EXPECT_EQ(trainInfo.status, trainInfoResult.status);
- ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
- EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
-}
-
-TEST(StorageManagerTest, TrainInfoReadWriteTrainNameSizeOneTest) {
- InstallTrainInfo trainInfo;
- trainInfo.trainVersionCode = 12345;
- trainInfo.trainName = "{";
- trainInfo.status = 1;
- const char* expIds = "test_ids";
- trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
-
- bool result;
-
- result = StorageManager::writeTrainInfo(trainInfo);
-
- EXPECT_TRUE(result);
-
- InstallTrainInfo trainInfoResult;
- result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
- EXPECT_TRUE(result);
-
- EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
- ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
- EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
- EXPECT_EQ(trainInfo.status, trainInfoResult.status);
- ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
- EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
-}
-
-TEST(StorageManagerTest, SortFileTest) {
- vector<StorageManager::FileInfo> list;
- // assume now sec is 500
- list.emplace_back("200_5000_123454", false, 20, 300);
- list.emplace_back("300_2000_123454_history", true, 30, 200);
- list.emplace_back("400_100009_123454_history", true, 40, 100);
- list.emplace_back("100_2000_123454", false, 50, 400);
-
- StorageManager::sortFiles(&list);
- EXPECT_EQ("200_5000_123454", list[0].mFileName);
- EXPECT_EQ("100_2000_123454", list[1].mFileName);
- EXPECT_EQ("400_100009_123454_history", list[2].mFileName);
- EXPECT_EQ("300_2000_123454_history", list[3].mFileName);
-}
-
-const string testDir = "/data/misc/stats-data/";
-const string file1 = testDir + "2557169347_1066_1";
-const string file2 = testDir + "2557169349_1066_1";
-const string file1_history = file1 + "_history";
-const string file2_history = file2 + "_history";
-
-bool prepareLocalHistoryTestFiles() {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(
- open(file1.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)));
- if (fd != -1) {
- dprintf(fd, "content");
- } else {
- return false;
- }
-
- android::base::unique_fd fd2(TEMP_FAILURE_RETRY(
- open(file2.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)));
- if (fd2 != -1) {
- dprintf(fd2, "content");
- } else {
- return false;
- }
- return true;
-}
-
-void clearLocalHistoryTestFiles() {
- TEMP_FAILURE_RETRY(remove(file1.c_str()));
- TEMP_FAILURE_RETRY(remove(file2.c_str()));
- TEMP_FAILURE_RETRY(remove(file1_history.c_str()));
- TEMP_FAILURE_RETRY(remove(file2_history.c_str()));
-}
-
-bool fileExist(string name) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY | O_CLOEXEC)));
- return fd != -1;
-}
-
-/* The following AppendConfigReportTests test the 4 combinations of [whether erase data] [whether
- * the caller is adb] */
-TEST(StorageManagerTest, AppendConfigReportTest1) {
- EXPECT_TRUE(prepareLocalHistoryTestFiles());
-
- ProtoOutputStream out;
- StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/,
- false /*isAdb?*/);
-
- EXPECT_FALSE(fileExist(file1));
- EXPECT_FALSE(fileExist(file2));
-
- EXPECT_TRUE(fileExist(file1_history));
- EXPECT_TRUE(fileExist(file2_history));
- clearLocalHistoryTestFiles();
-}
-
-TEST(StorageManagerTest, AppendConfigReportTest2) {
- EXPECT_TRUE(prepareLocalHistoryTestFiles());
-
- ProtoOutputStream out;
- StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/,
- false /*isAdb?*/);
-
- EXPECT_FALSE(fileExist(file1));
- EXPECT_FALSE(fileExist(file2));
- EXPECT_FALSE(fileExist(file1_history));
- EXPECT_FALSE(fileExist(file2_history));
-
- clearLocalHistoryTestFiles();
-}
-
-TEST(StorageManagerTest, AppendConfigReportTest3) {
- EXPECT_TRUE(prepareLocalHistoryTestFiles());
-
- ProtoOutputStream out;
- StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/,
- true /*isAdb?*/);
-
- EXPECT_TRUE(fileExist(file1));
- EXPECT_TRUE(fileExist(file2));
- EXPECT_FALSE(fileExist(file1_history));
- EXPECT_FALSE(fileExist(file2_history));
-
- clearLocalHistoryTestFiles();
-}
-
-TEST(StorageManagerTest, AppendConfigReportTest4) {
- EXPECT_TRUE(prepareLocalHistoryTestFiles());
-
- ProtoOutputStream out;
- StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/,
- true /*isAdb?*/);
-
- EXPECT_FALSE(fileExist(file1));
- EXPECT_FALSE(fileExist(file2));
- EXPECT_FALSE(fileExist(file1_history));
- EXPECT_FALSE(fileExist(file2_history));
-
- clearLocalHistoryTestFiles();
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
deleted file mode 100644
index 32cecd3b9dbc..000000000000
--- a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2020 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 "utils/MultiConditionTrigger.h"
-
-#include <gtest/gtest.h>
-
-#include <chrono>
-#include <set>
-#include <thread>
-#include <vector>
-
-#ifdef __ANDROID__
-
-using namespace std;
-using std::this_thread::sleep_for;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-TEST(MultiConditionTrigger, TestMultipleConditions) {
- int numConditions = 5;
- string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5";
- set<string> conditionNames = {t1, t2, t3, t4, t5};
-
- mutex lock;
- condition_variable cv;
- bool triggerCalled = false;
-
- // Mark done as true and notify in the done.
- MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
- {
- lock_guard lg(lock);
- triggerCalled = true;
- }
- cv.notify_all();
- });
-
- vector<thread> threads;
- vector<int> done(numConditions, 0);
-
- int i = 0;
- for (const string& conditionName : conditionNames) {
- threads.emplace_back([&done, &conditionName, &trigger, i] {
- sleep_for(chrono::milliseconds(3));
- done[i] = 1;
- trigger.markComplete(conditionName);
- });
- i++;
- }
-
- unique_lock<mutex> unique_lk(lock);
- cv.wait(unique_lk, [&triggerCalled] {
- return triggerCalled;
- });
-
- for (i = 0; i < numConditions; i++) {
- EXPECT_EQ(done[i], 1);
- }
-
- for (i = 0; i < numConditions; i++) {
- threads[i].join();
- }
-}
-
-TEST(MultiConditionTrigger, TestNoConditions) {
- mutex lock;
- condition_variable cv;
- bool triggerCalled = false;
-
- MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] {
- {
- lock_guard lg(lock);
- triggerCalled = true;
- }
- cv.notify_all();
- });
-
- unique_lock<mutex> unique_lk(lock);
- cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
- EXPECT_TRUE(triggerCalled);
- // Ensure that trigger occurs immediately if no events need to be completed.
-}
-
-TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) {
- string t1 = "t1", t2 = "t2";
- set<string> conditionNames = {t1, t2};
-
- mutex lock;
- condition_variable cv;
- bool triggerCalled = false;
-
- MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
- {
- lock_guard lg(lock);
- triggerCalled = true;
- }
- cv.notify_all();
- });
-
- trigger.markComplete(t1);
- trigger.markComplete(t1);
-
- // Ensure that the trigger still hasn't fired.
- {
- lock_guard lg(lock);
- EXPECT_FALSE(triggerCalled);
- }
-
- trigger.markComplete(t2);
- unique_lock<mutex> unique_lk(lock);
- cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
- EXPECT_TRUE(triggerCalled);
-}
-
-TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) {
- string t1 = "t1";
- set<string> conditionNames = {t1};
-
- mutex lock;
- condition_variable cv;
- bool triggerCalled = false;
- int triggerCount = 0;
-
- MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] {
- {
- lock_guard lg(lock);
- triggerCount++;
- triggerCalled = true;
- }
- cv.notify_all();
- });
-
- trigger.markComplete(t1);
-
- // Ensure that the trigger fired.
- {
- unique_lock<mutex> unique_lk(lock);
- cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
- EXPECT_TRUE(triggerCalled);
- EXPECT_EQ(triggerCount, 1);
- triggerCalled = false;
- }
-
- trigger.markComplete(t1);
-
- // Ensure that the trigger does not fire again.
- {
- unique_lock<mutex> unique_lk(lock);
- cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; });
- EXPECT_FALSE(triggerCalled);
- EXPECT_EQ(triggerCount, 1);
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp
deleted file mode 100644
index 69a43a8f4712..000000000000
--- a/cmds/statsd/tools/localtools/Android.bp
+++ /dev/null
@@ -1,46 +0,0 @@
-java_binary_host {
- name: "statsd_localdrive",
- manifest: "localdrive_manifest.txt",
- srcs: [
- "src/com/android/statsd/shelltools/localdrive/*.java",
- "src/com/android/statsd/shelltools/Utils.java",
- ],
- static_libs: [
- "platformprotos",
- "guava",
- ],
-}
-
-java_library_host {
- name: "statsd_testdrive_lib",
- srcs: [
- "src/com/android/statsd/shelltools/testdrive/*.java",
- "src/com/android/statsd/shelltools/Utils.java",
- ],
- static_libs: [
- "platformprotos",
- "guava",
- ],
-}
-
-
-java_binary_host {
- name: "statsd_testdrive",
- manifest: "testdrive_manifest.txt",
- static_libs: [
- "statsd_testdrive_lib",
- ],
-}
-
-java_test_host {
- name: "statsd_testdrive_test",
- test_suites: ["general-tests"],
- srcs: ["test/com/android/statsd/shelltools/testdrive/*.java"],
- static_libs: [
- "statsd_testdrive_lib",
- "junit",
- "platformprotos",
- "guava",
- ],
-}
-
diff --git a/cmds/statsd/tools/localtools/TEST_MAPPING b/cmds/statsd/tools/localtools/TEST_MAPPING
deleted file mode 100644
index 7c8a3db5c610..000000000000
--- a/cmds/statsd/tools/localtools/TEST_MAPPING
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "presubmit": [
- {
- "name": "statsd_testdrive_test",
- "host": true
- }
- ]
-}
diff --git a/cmds/statsd/tools/localtools/localdrive_manifest.txt b/cmds/statsd/tools/localtools/localdrive_manifest.txt
deleted file mode 100644
index 035cea1134bc..000000000000
--- a/cmds/statsd/tools/localtools/localdrive_manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-class: com.android.statsd.shelltools.localdrive.LocalDrive
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
deleted file mode 100644
index 6a74480b505e..000000000000
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.shelltools;
-
-import com.android.os.StatsLog.ConfigMetricsReportList;
-
-import com.google.common.io.Files;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Formatter;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Utilities for local use of statsd.
- */
-public class Utils {
-
- public static final String CMD_DUMP_REPORT = "cmd stats dump-report";
- public static final String CMD_LOG_APP_BREADCRUMB = "cmd stats log-app-breadcrumb";
- public static final String CMD_REMOVE_CONFIG = "cmd stats config remove";
- public static final String CMD_UPDATE_CONFIG = "cmd stats config update";
-
- public static final String SHELL_UID = "2000"; // Use shell, even if rooted.
-
- /**
- * Runs adb shell command with output directed to outputFile if non-null.
- */
- public static void runCommand(File outputFile, Logger logger, String... commands)
- throws IOException, InterruptedException {
- ProcessBuilder pb = new ProcessBuilder(commands);
- if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
- pb.redirectOutput(outputFile);
- }
- Process process = pb.start();
-
- // Capture any errors
- StringBuilder err = new StringBuilder();
- BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- for (String line = br.readLine(); line != null; line = br.readLine()) {
- err.append(line).append('\n');
- }
- logger.severe(err.toString());
-
- // Check result
- if (process.waitFor() == 0) {
- logger.fine("Adb command successful.");
- } else {
- logger.severe("Abnormal adb shell termination for: " + String.join(",", commands));
- throw new RuntimeException("Error running adb command: " + err.toString());
- }
- }
-
- /**
- * Dumps the report from the device and converts it to a ConfigMetricsReportList.
- * Erases the data if clearData is true.
- * @param configId id of the config
- * @param clearData whether to erase the report data from statsd after getting the report.
- * @param useShellUid Pulls data for the {@link SHELL_UID} instead of the caller's uid.
- * @param logger Logger to log error messages
- * @return
- * @throws IOException
- * @throws InterruptedException
- */
- public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
- boolean useShellUid, Logger logger, String deviceSerial)
- throws IOException, InterruptedException {
- try {
- File outputFile = File.createTempFile("statsdret", ".bin");
- outputFile.deleteOnExit();
- runCommand(
- outputFile,
- logger,
- "adb",
- "-s",
- deviceSerial,
- "shell",
- CMD_DUMP_REPORT,
- useShellUid ? SHELL_UID : "",
- String.valueOf(configId),
- clearData ? "" : "--keep_data",
- "--include_current_bucket",
- "--proto");
- ConfigMetricsReportList reportList =
- ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
- return reportList;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- logger.severe("Failed to fetch and parse the statsd output report. "
- + "Perhaps there is not a valid statsd config for the requested "
- + (useShellUid ? ("uid=" + SHELL_UID + ", ") : "")
- + "configId=" + configId
- + ".");
- throw (e);
- }
- }
-
- /**
- * Logs an AppBreadcrumbReported atom.
- * @param label which label to log for the app breadcrumb atom.
- * @param state which state to log for the app breadcrumb atom.
- * @param logger Logger to log error messages
- *
- * @throws IOException
- * @throws InterruptedException
- */
- public static void logAppBreadcrumb(int label, int state, Logger logger, String deviceSerial)
- throws IOException, InterruptedException {
- runCommand(
- null,
- logger,
- "adb",
- "-s",
- deviceSerial,
- "shell",
- CMD_LOG_APP_BREADCRUMB,
- String.valueOf(label),
- String.valueOf(state));
- }
- public static void setUpLogger(Logger logger, boolean debug) {
- ConsoleHandler handler = new ConsoleHandler();
- handler.setFormatter(new LocalToolsFormatter());
- logger.setUseParentHandlers(false);
- if (debug) {
- handler.setLevel(Level.ALL);
- logger.setLevel(Level.ALL);
- }
- logger.addHandler(handler);
- }
-
- /**
- * Attempt to determine whether tool will work with this statsd, i.e. whether statsd is
- * minCodename or higher.
- * Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName))
- * If all else fails, assume it will work (letting future commands deal with any errors).
- */
- public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename,
- String deviceSerial) {
- BufferedReader in = null;
- try {
- File outFileSdk = File.createTempFile("shelltools_sdk", "tmp");
- outFileSdk.deleteOnExit();
- runCommand(outFileSdk, logger,
- "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.sdk");
- in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk)));
- // If NullPointerException/NumberFormatException/etc., just catch and return true.
- int sdk = Integer.parseInt(in.readLine().trim());
- if (sdk >= minSdk) {
- return true;
- } else if (sdk == minSdk - 1) { // Could be minSdk-1, or could be minSdk development.
- in.close();
- File outFileCode = File.createTempFile("shelltools_codename", "tmp");
- outFileCode.deleteOnExit();
- runCommand(outFileCode, logger,
- "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.codename");
- in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode)));
- return in.readLine().startsWith(minCodename);
- } else {
- return false;
- }
- } catch (Exception e) {
- logger.fine("Could not determine whether statsd version is compatibile "
- + "with tool: " + e.toString());
- } finally {
- try {
- if (in != null) {
- in.close();
- }
- } catch (IOException e) {
- logger.fine("Could not close temporary file: " + e.toString());
- }
- }
- // Could not determine whether statsd is acceptable version.
- // Just assume it is; if it isn't, we'll just get future errors via adb and deal with them.
- return true;
- }
-
- public static class LocalToolsFormatter extends Formatter {
- public String format(LogRecord record) {
- return record.getMessage() + "\n";
- }
- }
-
- /**
- * Parse the result of "adb devices" to return the list of connected devices.
- * @param logger Logger to log error messages
- * @return List of the serial numbers of the connected devices.
- */
- public static List<String> getDeviceSerials(Logger logger) {
- try {
- ArrayList<String> devices = new ArrayList<>();
- File outFile = File.createTempFile("device_serial", "tmp");
- outFile.deleteOnExit();
- Utils.runCommand(outFile, logger, "adb", "devices");
- List<String> outputLines = Files.readLines(outFile, Charset.defaultCharset());
- Pattern regex = Pattern.compile("^(.*)\tdevice$");
- for (String line : outputLines) {
- Matcher m = regex.matcher(line);
- if (m.find()) {
- devices.add(m.group(1));
- }
- }
- return devices;
- } catch (Exception ex) {
- logger.log(Level.SEVERE, "Failed to list connected devices: " + ex.getMessage());
- }
- return null;
- }
-
- /**
- * Returns ANDROID_SERIAL environment variable, or null if that is undefined or unavailable.
- * @param logger Destination of error messages.
- * @return String value of ANDROID_SERIAL environment variable, or null.
- */
- public static String getDefaultDevice(Logger logger) {
- try {
- return System.getenv("ANDROID_SERIAL");
- } catch (Exception ex) {
- logger.log(Level.SEVERE, "Failed to check ANDROID_SERIAL environment variable.",
- ex);
- }
- return null;
- }
-
- /**
- * Returns the device to use if one can be deduced, or null.
- * @param device Command-line specified device, or null.
- * @param connectedDevices List of all connected devices.
- * @param defaultDevice Environment-variable specified device, or null.
- * @param logger Destination of error messages.
- * @return Device to use, or null.
- */
- public static String chooseDevice(String device, List<String> connectedDevices,
- String defaultDevice, Logger logger) {
- if (connectedDevices == null || connectedDevices.isEmpty()) {
- logger.severe("No connected device.");
- return null;
- }
- if (device != null) {
- if (connectedDevices.contains(device)) {
- return device;
- }
- logger.severe("Device not connected: " + device);
- return null;
- }
- if (connectedDevices.size() == 1) {
- return connectedDevices.get(0);
- }
- if (defaultDevice != null) {
- if (connectedDevices.contains(defaultDevice)) {
- return defaultDevice;
- } else {
- logger.severe("ANDROID_SERIAL device is not connected: " + defaultDevice);
- return null;
- }
- }
- logger.severe("More than one device is connected. Choose one"
- + " with -s DEVICE_SERIAL or environment variable ANDROID_SERIAL.");
- return null;
- }
-}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
deleted file mode 100644
index ec3c7df7bfba..000000000000
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.shelltools.localdrive;
-
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.statsd.shelltools.Utils;
-
-import com.google.common.io.Files;
-import com.google.protobuf.TextFormat;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.List;
-import java.util.logging.Logger;
-
-/**
- * Tool for using statsd locally. Can upload a config and get the data. Handles
- * both binary and human-readable protos.
- * To make: make statsd_localdrive
- * To run: statsd_localdrive (i.e. ./out/host/linux-x86/bin/statsd_localdrive)
- */
-public class LocalDrive {
- private static final boolean DEBUG = false;
-
- public static final int MIN_SDK = 29;
- public static final String MIN_CODENAME = "Q";
-
- public static final long DEFAULT_CONFIG_ID = 56789;
-
- public static final String BINARY_FLAG = "--binary";
- public static final String CLEAR_DATA = "--clear";
- public static final String NO_UID_MAP_FLAG = "--no-uid-map";
-
- public static final String HELP_STRING =
- "Usage:\n\n" +
-
- "statsd_localdrive [-s DEVICE_SERIAL] upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
- " Uploads the given statsd config file (in binary or human-readable-text format).\n" +
- " If a config with this id already exists, removes it first.\n" +
- " CONFIG_FILE Location of config file on host.\n" +
- " CONFIG_ID Long ID to associate with this config. If absent, uses "
- + DEFAULT_CONFIG_ID + ".\n" +
- " --binary Config is in binary format; otherwise, assumed human-readable text.\n" +
- // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
- "\n" +
-
- "statsd_localdrive [-s DEVICE_SERIAL] update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
- " Same as upload, but does not remove the old config first (if it already exists).\n" +
- // Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
- "\n" +
-
- "statsd_localdrive [-s DEVICE_SERIAL] get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
- " Prints the output statslog data (in binary or human-readable-text format).\n" +
- " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
- " --binary Output should be in binary, instead of default human-readable text.\n" +
- " Binary output can be redirected as usual (e.g. > FILENAME).\n" +
- " --no-uid-map Do not include the uid-map (the very lengthy uid<-->pkgName map).\n" +
- " --clear Erase the data from statsd afterwards. Does not remove the config.\n" +
- // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID [--keep_data]
- // --include_current_bucket --proto
- "\n" +
-
- "statsd_localdrive [-s DEVICE_SERIAL] remove [CONFIG_ID]\n" +
- " Removes the config.\n" +
- " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
- // Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
- "\n" +
-
- "statsd_localdrive [-s DEVICE_SERIAL] clear [CONFIG_ID]\n" +
- " Clears the data associated with the config.\n" +
- " CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
- // Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
- // --include_current_bucket --proto
- "";
-
-
- private static final Logger sLogger = Logger.getLogger(LocalDrive.class.getName());
-
- /** Usage: make statsd_localdrive && statsd_localdrive */
- public static void main(String[] args) {
- Utils.setUpLogger(sLogger, DEBUG);
- if (args.length == 0) {
- printHelp();
- return;
- }
-
- int remainingArgsLength = args.length;
- String deviceSerial = null;
- if (args[0].equals("-s")) {
- if (args.length == 1) {
- printHelp();
- }
- deviceSerial = args[1];
- remainingArgsLength -= 2;
- }
-
- List<String> connectedDevices = Utils.getDeviceSerials(sLogger);
- deviceSerial = Utils.chooseDevice(deviceSerial, connectedDevices,
- Utils.getDefaultDevice(sLogger), sLogger);
- if (deviceSerial == null) {
- return;
- }
-
- if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME, deviceSerial)) {
- sLogger.severe("LocalDrive only works with statsd versions for Android "
- + MIN_CODENAME + " or higher.");
- return;
- }
-
- int idx = args.length - remainingArgsLength;
- if (remainingArgsLength > 0) {
- switch (args[idx]) {
- case "clear":
- cmdClear(args, idx, deviceSerial);
- return;
- case "get-data":
- cmdGetData(args, idx, deviceSerial);
- return;
- case "remove":
- cmdRemove(args, idx);
- return;
- case "update":
- cmdUpdate(args, idx, deviceSerial);
- return;
- case "upload":
- cmdUpload(args, idx, deviceSerial);
- return;
- }
- }
- printHelp();
- }
-
- private static void printHelp() {
- sLogger.info(HELP_STRING);
- }
-
- // upload CONFIG_FILE [CONFIG_ID] [--binary]
- private static boolean cmdUpload(String[] args, int idx, String deviceSerial) {
- return updateConfig(args, idx, true, deviceSerial);
- }
-
- // update CONFIG_FILE [CONFIG_ID] [--binary]
- private static boolean cmdUpdate(String[] args, int idx, String deviceSerial) {
- return updateConfig(args, idx, false, deviceSerial);
- }
-
- private static boolean updateConfig(String[] args, int idx, boolean removeOldConfig,
- String deviceSerial) {
- int argCount = args.length - 1 - idx; // Used up one for upload/update.
-
- // Get CONFIG_FILE
- if (argCount < 1) {
- sLogger.severe("No config file provided.");
- printHelp();
- return false;
- }
- final String origConfigLocation = args[idx + 1];
- if (!new File(origConfigLocation).exists()) {
- sLogger.severe("Error - Cannot find the provided config file: " + origConfigLocation);
- return false;
- }
- argCount--;
-
- // Get --binary
- boolean binary = contains(args, idx + 2, BINARY_FLAG);
- if (binary) argCount --;
-
- // Get CONFIG_ID
- long configId;
- try {
- configId = getConfigId(argCount < 1, args, idx + 2);
- } catch (NumberFormatException e) {
- sLogger.severe("Invalid config id provided.");
- printHelp();
- return false;
- }
- sLogger.fine(String.format("updateConfig with %s %d %b %b",
- origConfigLocation, configId, binary, removeOldConfig));
-
- // Remove the old config.
- if (removeOldConfig) {
- try {
- Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
- Utils.SHELL_UID, String.valueOf(configId));
- Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger,
- deviceSerial);
- } catch (InterruptedException | IOException e) {
- sLogger.severe("Failed to remove config: " + e.getMessage());
- return false;
- }
- }
-
- // Upload the config.
- String configLocation;
- if (binary) {
- configLocation = origConfigLocation;
- } else {
- StatsdConfig.Builder builder = StatsdConfig.newBuilder();
- try {
- TextFormat.merge(new FileReader(origConfigLocation), builder);
- } catch (IOException e) {
- sLogger.severe("Failed to read config file " + origConfigLocation + ": "
- + e.getMessage());
- return false;
- }
-
- try {
- File tempConfigFile = File.createTempFile("statsdconfig", ".config");
- tempConfigFile.deleteOnExit();
- Files.write(builder.build().toByteArray(), tempConfigFile);
- configLocation = tempConfigFile.getAbsolutePath();
- } catch (IOException e) {
- sLogger.severe("Failed to write temp config file: " + e.getMessage());
- return false;
- }
- }
- String remotePath = "/data/local/tmp/statsdconfig.config";
- try {
- Utils.runCommand(null, sLogger, "adb", "push", configLocation, remotePath);
- Utils.runCommand(null, sLogger, "adb", "shell", "cat", remotePath, "|",
- Utils.CMD_UPDATE_CONFIG, Utils.SHELL_UID, String.valueOf(configId));
- } catch (InterruptedException | IOException e) {
- sLogger.severe("Failed to update config: " + e.getMessage());
- return false;
- }
- return true;
- }
-
- // get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]
- private static boolean cmdGetData(String[] args, int idx, String deviceSerial) {
- boolean binary = contains(args, idx + 1, BINARY_FLAG);
- boolean noUidMap = contains(args, idx + 1, NO_UID_MAP_FLAG);
- boolean clearData = contains(args, idx + 1, CLEAR_DATA);
-
- // Get CONFIG_ID
- int argCount = args.length - 1 - idx; // Used up one for get-data.
- if (binary) argCount--;
- if (noUidMap) argCount--;
- if (clearData) argCount--;
- long configId;
- try {
- configId = getConfigId(argCount < 1, args, idx + 1);
- } catch (NumberFormatException e) {
- sLogger.severe("Invalid config id provided.");
- printHelp();
- return false;
- }
- sLogger.fine(String.format("cmdGetData with %d %b %b %b",
- configId, clearData, binary, noUidMap));
-
- // Get the StatsLog
- // Even if the args request no modifications, we still parse it to make sure it's valid.
- ConfigMetricsReportList reportList;
- try {
- reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger,
- deviceSerial);
- } catch (IOException | InterruptedException e) {
- sLogger.severe("Failed to get report list: " + e.getMessage());
- return false;
- }
- if (noUidMap) {
- ConfigMetricsReportList.Builder builder
- = ConfigMetricsReportList.newBuilder(reportList);
- // Clear the reports, then add them back without their UidMap.
- builder.clearReports();
- for (ConfigMetricsReport report : reportList.getReportsList()) {
- builder.addReports(ConfigMetricsReport.newBuilder(report).clearUidMap());
- }
- reportList = builder.build();
- }
-
- if (!binary) {
- sLogger.info(reportList.toString());
- } else {
- try {
- System.out.write(reportList.toByteArray());
- } catch (IOException e) {
- sLogger.severe("Failed to output binary statslog proto: "
- + e.getMessage());
- return false;
- }
- }
- return true;
- }
-
- // clear [CONFIG_ID]
- private static boolean cmdClear(String[] args, int idx, String deviceSerial) {
- // Get CONFIG_ID
- long configId;
- try {
- configId = getConfigId(false, args, idx + 1);
- } catch (NumberFormatException e) {
- sLogger.severe("Invalid config id provided.");
- printHelp();
- return false;
- }
- sLogger.fine(String.format("cmdClear with %d", configId));
-
- try {
- Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger,
- deviceSerial);
- } catch (IOException | InterruptedException e) {
- sLogger.severe("Failed to get report list: " + e.getMessage());
- return false;
- }
- return true;
- }
-
- // remove [CONFIG_ID]
- private static boolean cmdRemove(String[] args, int idx) {
- // Get CONFIG_ID
- long configId;
- try {
- configId = getConfigId(false, args, idx + 1);
- } catch (NumberFormatException e) {
- sLogger.severe("Invalid config id provided.");
- printHelp();
- return false;
- }
- sLogger.fine(String.format("cmdRemove with %d", configId));
-
- try {
- Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
- Utils.SHELL_UID, String.valueOf(configId));
- } catch (InterruptedException | IOException e) {
- sLogger.severe("Failed to remove config: " + e.getMessage());
- return false;
- }
- return true;
- }
-
- /**
- * Searches through the array to see if it contains (precisely) the given value, starting
- * at the given firstIdx.
- */
- private static boolean contains(String[] array, int firstIdx, String value) {
- if (value == null) return false;
- if (firstIdx < 0) return false;
- for (int i = firstIdx; i < array.length; i++) {
- if (value.equals(array[i])) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Gets the config id from args[idx], or returns DEFAULT_CONFIG_ID if args[idx] does not exist.
- * If justUseDefault, overrides and just uses DEFAULT_CONFIG_ID instead.
- */
- private static long getConfigId(boolean justUseDefault, String[] args, int idx)
- throws NumberFormatException {
- if (justUseDefault || args.length <= idx || idx < 0) {
- return DEFAULT_CONFIG_ID;
- }
- try {
- return Long.valueOf(args[idx]);
- } catch (NumberFormatException e) {
- sLogger.severe("Bad config id provided: " + args[idx]);
- throw e;
- }
- }
-}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
deleted file mode 100644
index 51bcad115cc5..000000000000
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.shelltools.testdrive;
-
-import com.android.internal.os.StatsdConfigProto;
-import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.EventMetric;
-import com.android.internal.os.StatsdConfigProto.FieldFilter;
-import com.android.internal.os.StatsdConfigProto.GaugeMetric;
-import com.android.internal.os.StatsdConfigProto.PullAtomPackages;
-import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import com.android.os.AtomsProto.Atom;
-import com.android.os.StatsLog;
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.StatsLogReport;
-import com.android.statsd.shelltools.Utils;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.io.Files;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class TestDrive {
-
- private static final int METRIC_ID_BASE = 1111;
- private static final long ATOM_MATCHER_ID_BASE = 1234567;
- private static final long APP_BREADCRUMB_MATCHER_ID = 1111111;
- private static final int PULL_ATOM_START = 10000;
- private static final int MAX_PLATFORM_ATOM_TAG = 100000;
- private static final int VENDOR_PULLED_ATOM_START_TAG = 150000;
- private static final long CONFIG_ID = 54321;
- private static final String[] ALLOWED_LOG_SOURCES = {
- "AID_GRAPHICS",
- "AID_INCIDENTD",
- "AID_STATSD",
- "AID_RADIO",
- "com.android.systemui",
- "com.android.vending",
- "AID_SYSTEM",
- "AID_ROOT",
- "AID_BLUETOOTH",
- "AID_LMKD",
- "com.android.managedprovisioning",
- "AID_MEDIA",
- "AID_NETWORK_STACK",
- "com.google.android.providers.media.module",
- };
- private static final String[] DEFAULT_PULL_SOURCES = {
- "AID_SYSTEM",
- "AID_RADIO"
- };
- private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
-
- @VisibleForTesting
- String mDeviceSerial = null;
- @VisibleForTesting
- Dumper mDumper = new BasicDumper();
-
- public static void main(String[] args) {
- final Configuration configuration = new Configuration();
- final TestDrive testDrive = new TestDrive();
- Utils.setUpLogger(LOGGER, false);
-
- if (!testDrive.processArgs(configuration, args,
- Utils.getDeviceSerials(LOGGER), Utils.getDefaultDevice(LOGGER))) {
- return;
- }
-
- final ConfigMetricsReportList reports = testDrive.testDriveAndGetReports(
- configuration.createConfig(), configuration.hasPulledAtoms(),
- configuration.hasPushedAtoms());
- if (reports != null) {
- configuration.dumpMetrics(reports, testDrive.mDumper);
- }
- }
-
- boolean processArgs(Configuration configuration, String[] args, List<String> connectedDevices,
- String defaultDevice) {
- if (args.length < 1) {
- LOGGER.severe("Usage: ./test_drive [-one] "
- + "[-p additional_allowed_package] "
- + "[-s DEVICE_SERIAL_NUMBER] "
- + "<atomId1> <atomId2> ... <atomIdN>");
- return false;
- }
-
- int first_arg = 0;
- // Consume all flags, which must precede all atoms
- for (; first_arg < args.length; ++first_arg) {
- String arg = args[first_arg];
- int remaining_args = args.length - first_arg;
- if (remaining_args >= 2 && arg.equals("-one")) {
- LOGGER.info("Creating one event metric to catch all pushed atoms.");
- configuration.mOnePushedAtomEvent = true;
- } else if (remaining_args >= 2 && arg.equals("-terse")) {
- LOGGER.info("Terse output format.");
- mDumper = new TerseDumper();
- } else if (remaining_args >= 3 && arg.equals("-p")) {
- configuration.mAdditionalAllowedPackage = args[++first_arg];
- } else if (remaining_args >= 3 && arg.equals("-s")) {
- mDeviceSerial = args[++first_arg];
- } else {
- break; // Found the atom list
- }
- }
-
- mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER);
- if (mDeviceSerial == null) {
- return false;
- }
-
- for ( ; first_arg < args.length; ++first_arg) {
- String atom = args[first_arg];
- try {
- configuration.addAtom(Integer.valueOf(atom));
- } catch (NumberFormatException e) {
- LOGGER.severe("Bad atom id provided: " + atom);
- }
- }
-
- return configuration.hasPulledAtoms() || configuration.hasPushedAtoms();
- }
-
- private ConfigMetricsReportList testDriveAndGetReports(StatsdConfig config,
- boolean hasPulledAtoms, boolean hasPushedAtoms) {
- if (config == null) {
- LOGGER.severe("Failed to create valid config.");
- return null;
- }
-
- String remoteConfigPath = null;
- try {
- remoteConfigPath = pushConfig(config, mDeviceSerial);
- LOGGER.info("Pushed the following config to statsd on device '" + mDeviceSerial
- + "':");
- LOGGER.info(config.toString());
- if (hasPushedAtoms) {
- LOGGER.info("Now please play with the device to trigger the event.");
- }
- if (!hasPulledAtoms) {
- LOGGER.info(
- "All events should be dumped after 1 min ...");
- Thread.sleep(60_000);
- } else {
- LOGGER.info("All events should be dumped after 1.5 minutes ...");
- Thread.sleep(15_000);
- Utils.logAppBreadcrumb(0, 0, LOGGER, mDeviceSerial);
- Thread.sleep(75_000);
- }
- return Utils.getReportList(CONFIG_ID, true, false, LOGGER,
- mDeviceSerial);
- } catch (Exception e) {
- LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
- } finally {
- removeConfig(mDeviceSerial);
- if (remoteConfigPath != null) {
- try {
- Utils.runCommand(null, LOGGER,
- "adb", "-s", mDeviceSerial, "shell", "rm",
- remoteConfigPath);
- } catch (Exception e) {
- LOGGER.log(Level.WARNING,
- "Unable to remove remote config file: " + remoteConfigPath, e);
- }
- }
- }
- return null;
- }
-
- static class Configuration {
- boolean mOnePushedAtomEvent = false;
- @VisibleForTesting
- Set<Integer> mPushedAtoms = new TreeSet<>();
- @VisibleForTesting
- Set<Integer> mPulledAtoms = new TreeSet<>();
- @VisibleForTesting
- String mAdditionalAllowedPackage = null;
- private final Set<Long> mTrackedMetrics = new HashSet<>();
-
- private void dumpMetrics(ConfigMetricsReportList reportList, Dumper dumper) {
- // We may get multiple reports. Take the last one.
- ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
- for (StatsLogReport statsLog : report.getMetricsList()) {
- if (isTrackedMetric(statsLog.getMetricId())) {
- dumper.dump(statsLog);
- }
- }
- }
-
- boolean isTrackedMetric(long metricId) {
- return mTrackedMetrics.contains(metricId);
- }
-
- static boolean isPulledAtom(int atomId) {
- return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG
- || atomId >= VENDOR_PULLED_ATOM_START_TAG;
- }
-
- void addAtom(Integer atom) {
- if (Atom.getDescriptor().findFieldByNumber(atom) == null) {
- LOGGER.severe("No such atom found: " + atom);
- return;
- }
- if (isPulledAtom(atom)) {
- mPulledAtoms.add(atom);
- } else {
- mPushedAtoms.add(atom);
- }
- }
-
- private boolean hasPulledAtoms() {
- return !mPulledAtoms.isEmpty();
- }
-
- private boolean hasPushedAtoms() {
- return !mPushedAtoms.isEmpty();
- }
-
- StatsdConfig createConfig() {
- long metricId = METRIC_ID_BASE;
- long atomMatcherId = ATOM_MATCHER_ID_BASE;
-
- StatsdConfig.Builder builder = baseBuilder();
-
- if (hasPulledAtoms()) {
- builder.addAtomMatcher(
- createAtomMatcher(
- Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
- APP_BREADCRUMB_MATCHER_ID));
- }
-
- for (int atomId : mPulledAtoms) {
- builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
- GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
- gaugeMetricBuilder
- .setId(metricId)
- .setWhat(atomMatcherId)
- .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID)
- .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
- .setBucket(TimeUnit.ONE_MINUTE)
- .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
- .setMaxNumGaugeAtomsPerBucket(100);
- builder.addGaugeMetric(gaugeMetricBuilder.build());
- atomMatcherId++;
- mTrackedMetrics.add(metricId++);
- }
-
- // A simple atom matcher for each pushed atom.
- List<AtomMatcher> simpleAtomMatchers = new ArrayList<>();
- for (int atomId : mPushedAtoms) {
- final AtomMatcher atomMatcher = createAtomMatcher(atomId, atomMatcherId++);
- simpleAtomMatchers.add(atomMatcher);
- builder.addAtomMatcher(atomMatcher);
- }
-
- if (mOnePushedAtomEvent) {
- // Create a union event metric, using an matcher that matches all pulled atoms.
- AtomMatcher unionAtomMatcher = createUnionMatcher(simpleAtomMatchers,
- atomMatcherId);
- builder.addAtomMatcher(unionAtomMatcher);
- EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
- eventMetricBuilder.setId(metricId).setWhat(unionAtomMatcher.getId());
- builder.addEventMetric(eventMetricBuilder.build());
- mTrackedMetrics.add(metricId++);
- } else {
- // Create multiple event metrics, one per pulled atom.
- for (AtomMatcher atomMatcher : simpleAtomMatchers) {
- EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
- eventMetricBuilder
- .setId(metricId)
- .setWhat(atomMatcher.getId());
- builder.addEventMetric(eventMetricBuilder.build());
- mTrackedMetrics.add(metricId++);
- }
- }
-
- return builder.build();
- }
-
- private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
- AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
- atomMatcherBuilder
- .setId(matcherId)
- .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
- return atomMatcherBuilder.build();
- }
-
- private AtomMatcher createUnionMatcher(List<AtomMatcher> simpleAtomMatchers,
- long atomMatcherId) {
- AtomMatcher.Combination.Builder combinationBuilder =
- AtomMatcher.Combination.newBuilder();
- combinationBuilder.setOperation(StatsdConfigProto.LogicalOperation.OR);
- for (AtomMatcher matcher : simpleAtomMatchers) {
- combinationBuilder.addMatcher(matcher.getId());
- }
- AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
- atomMatcherBuilder.setId(atomMatcherId).setCombination(combinationBuilder.build());
- return atomMatcherBuilder.build();
- }
-
- private StatsdConfig.Builder baseBuilder() {
- ArrayList<String> allowedSources = new ArrayList<>();
- Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
- if (mAdditionalAllowedPackage != null) {
- allowedSources.add(mAdditionalAllowedPackage);
- }
- return StatsdConfig.newBuilder()
- .addAllAllowedLogSource(allowedSources)
- .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES))
- .addPullAtomPackages(PullAtomPackages.newBuilder()
- .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER)
- .addPackages("AID_GPU_SERVICE"))
- .addPullAtomPackages(PullAtomPackages.newBuilder()
- .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER)
- .addPackages("AID_GPU_SERVICE"))
- .addPullAtomPackages(PullAtomPackages.newBuilder()
- .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
- .addPackages("AID_STATSD"))
- .addPullAtomPackages(PullAtomPackages.newBuilder()
- .setAtomId(Atom.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS_FIELD_NUMBER)
- .addPackages("com.google.android.providers.media.module"))
- .setHashStringsInMetricReport(false);
- }
- }
-
- interface Dumper {
- void dump(StatsLogReport report);
- }
-
- static class BasicDumper implements Dumper {
- @Override
- public void dump(StatsLogReport report) {
- System.out.println(report.toString());
- }
- }
-
- static class TerseDumper extends BasicDumper {
- @Override
- public void dump(StatsLogReport report) {
- if (report.hasGaugeMetrics()) {
- dumpGaugeMetrics(report);
- }
- if (report.hasEventMetrics()) {
- dumpEventMetrics(report);
- }
- }
- void dumpEventMetrics(StatsLogReport report) {
- final List<StatsLog.EventMetricData> data = report.getEventMetrics().getDataList();
- if (data.isEmpty()) {
- return;
- }
- long firstTimestampNanos = data.get(0).getElapsedTimestampNanos();
- for (StatsLog.EventMetricData event : data) {
- final double deltaSec = (event.getElapsedTimestampNanos() - firstTimestampNanos)
- / 1e9;
- System.out.println(
- String.format("+%.3fs: %s", deltaSec, event.getAtom().toString()));
- }
- }
- void dumpGaugeMetrics(StatsLogReport report) {
- final List<StatsLog.GaugeMetricData> data = report.getGaugeMetrics().getDataList();
- if (data.isEmpty()) {
- return;
- }
- for (StatsLog.GaugeMetricData gauge : data) {
- System.out.println(gauge.toString());
- }
- }
- }
-
- private static String pushConfig(StatsdConfig config, String deviceSerial)
- throws IOException, InterruptedException {
- File configFile = File.createTempFile("statsdconfig", ".config");
- configFile.deleteOnExit();
- Files.write(config.toByteArray(), configFile);
- String remotePath = "/data/local/tmp/" + configFile.getName();
- Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
- "push", configFile.getAbsolutePath(), remotePath);
- Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
- "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
- String.valueOf(CONFIG_ID));
- return remotePath;
- }
-
- private static void removeConfig(String deviceSerial) {
- try {
- Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
- "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
- } catch (Exception e) {
- LOGGER.severe("Failed to remove config: " + e.getMessage());
- }
- }
-}
diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java
deleted file mode 100644
index b1cc60f74993..000000000000
--- a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2020 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.statsd.shelltools.testdrive;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.internal.os.StatsdConfigProto;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.os.AtomsProto;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Tests for {@link TestDrive}
- */
-public class ConfigurationTest {
-
- private StatsdConfigProto.AtomMatcher findAndRemoveAtomMatcherById(
- List<StatsdConfigProto.AtomMatcher> atomMatchers, long id) {
- int numMatches = 0;
- StatsdConfigProto.AtomMatcher match = null;
- for (StatsdConfigProto.AtomMatcher atomMatcher : atomMatchers) {
- if (id == atomMatcher.getId()) {
- ++numMatches;
- match = atomMatcher;
- }
- }
- if (numMatches == 1) {
- atomMatchers.remove(match);
- return match;
- }
- return null; // Too many, or not found
- }
-
- private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration();
-
- @Test
- public void testOnePushed() {
- final int atom = 90;
- assertFalse(TestDrive.Configuration.isPulledAtom(atom));
- mConfiguration.addAtom(atom);
- StatsdConfig config = mConfiguration.createConfig();
-
- //event_metric {
- // id: 1111
- // what: 1234567
- //}
- //atom_matcher {
- // id: 1234567
- // simple_atom_matcher {
- // atom_id: 90
- // }
- //}
-
- assertEquals(1, config.getEventMetricCount());
- assertEquals(0, config.getGaugeMetricCount());
-
- assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId()));
-
- final List<StatsdConfigProto.AtomMatcher> atomMatchers =
- new ArrayList<>(config.getAtomMatcherList());
- assertEquals(atom,
- findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat())
- .getSimpleAtomMatcher().getAtomId());
- assertEquals(0, atomMatchers.size());
- }
-
- @Test
- public void testOnePulled() {
- final int atom = 10022;
- assertTrue(TestDrive.Configuration.isPulledAtom(atom));
- mConfiguration.addAtom(atom);
- StatsdConfig config = mConfiguration.createConfig();
-
- //gauge_metric {
- // id: 1111
- // what: 1234567
- // gauge_fields_filter {
- // include_all: true
- // }
- // bucket: ONE_MINUTE
- // sampling_type: FIRST_N_SAMPLES
- // max_num_gauge_atoms_per_bucket: 100
- // trigger_event: 1111111
- //}
- //atom_matcher {
- // id: 1111111
- // simple_atom_matcher {
- // atom_id: 47
- // }
- //}
- //atom_matcher {
- // id: 1234567
- // simple_atom_matcher {
- // atom_id: 10022
- // }
- //}
-
- assertEquals(0, config.getEventMetricCount());
- assertEquals(1, config.getGaugeMetricCount());
-
- assertTrue(mConfiguration.isTrackedMetric(config.getGaugeMetric(0).getId()));
-
- final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
- assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
-
- final List<StatsdConfigProto.AtomMatcher> atomMatchers =
- new ArrayList<>(config.getAtomMatcherList());
- assertEquals(atom,
- findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
- .getSimpleAtomMatcher().getAtomId());
- assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
- findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
- .getSimpleAtomMatcher().getAtomId());
- assertEquals(0, atomMatchers.size());
- }
-
- @Test
- public void testOnePulledTwoPushed() {
- final int pulledAtom = 10022;
- assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom));
- mConfiguration.addAtom(pulledAtom);
-
- Integer[] pushedAtoms = new Integer[]{244, 245};
- for (int atom : pushedAtoms) {
- assertFalse(TestDrive.Configuration.isPulledAtom(atom));
- mConfiguration.addAtom(atom);
- }
- StatsdConfig config = mConfiguration.createConfig();
-
- // event_metric {
- // id: 1111
- // what: 1234567
- // }
- // event_metric {
- // id: 1112
- // what: 1234568
- // }
- // gauge_metric {
- // id: 1114
- // what: 1234570
- // gauge_fields_filter {
- // include_all: true
- // }
- // bucket: ONE_MINUTE
- // sampling_type: FIRST_N_SAMPLES
- // max_num_gauge_atoms_per_bucket: 100
- // trigger_event: 1111111
- // }
- // atom_matcher {
- // id: 1111111
- // simple_atom_matcher {
- // atom_id: 47
- // }
- // }
- // atom_matcher {
- // id: 1234567
- // simple_atom_matcher {
- // atom_id: 244
- // }
- // }
- // atom_matcher {
- // id: 1234568
- // simple_atom_matcher {
- // atom_id: 245
- // }
- // }
- // atom_matcher {
- // id: 1234570
- // simple_atom_matcher {
- // atom_id: 10022
- // }
- // }
-
- assertEquals(2, config.getEventMetricCount());
- assertEquals(1, config.getGaugeMetricCount());
-
- final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
- assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId()));
- assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
- for (StatsdConfigProto.EventMetric eventMetric : config.getEventMetricList()) {
- assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId()));
- }
-
- final List<StatsdConfigProto.AtomMatcher> atomMatchers =
- new ArrayList<>(config.getAtomMatcherList());
-
- assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
- .getSimpleAtomMatcher().getAtomId());
- assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
- findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
- .getSimpleAtomMatcher().getAtomId());
-
- Integer[] actualAtoms = new Integer[]{
- findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat())
- .getSimpleAtomMatcher().getAtomId(),
- findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(1).getWhat())
- .getSimpleAtomMatcher().getAtomId()};
- Arrays.sort(actualAtoms);
- assertArrayEquals(pushedAtoms, actualAtoms);
-
- assertEquals(0, atomMatchers.size());
- }
-
- @Test
- public void testOnePulledTwoPushedTogether() {
- mConfiguration.mOnePushedAtomEvent = true; // Use one event grabbing all pushed atoms
-
- final int pulledAtom = 10022;
- assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom));
- mConfiguration.addAtom(pulledAtom);
-
- Integer[] pushedAtoms = new Integer[]{244, 245};
- for (int atom : pushedAtoms) {
- assertFalse(TestDrive.Configuration.isPulledAtom(atom));
- mConfiguration.addAtom(atom);
- }
- StatsdConfig config = mConfiguration.createConfig();
-
- // event_metric {
- // id: 1112
- // what: 1234570
- // }
- // gauge_metric {
- // id: 1111
- // what: 1234567
- // gauge_fields_filter {
- // include_all: true
- // }
- // bucket: ONE_MINUTE
- // sampling_type: FIRST_N_SAMPLES
- // max_num_gauge_atoms_per_bucket: 100
- // trigger_event: 1111111
- // }
- // atom_matcher {
- // id: 1111111
- // simple_atom_matcher {
- // atom_id: 47
- // }
- // }
- // atom_matcher {
- // id: 1234567
- // simple_atom_matcher {
- // atom_id: 10022
- // }
- // }
- // atom_matcher {
- // id: 1234568
- // simple_atom_matcher {
- // atom_id: 244
- // }
- // }
- // atom_matcher {
- // id: 1234569
- // simple_atom_matcher {
- // atom_id: 245
- // }
- // }
- // atom_matcher {
- // id: 1234570
- // combination {
- // operation: OR
- // matcher: 1234568
- // matcher: 1234569
- // }
- // }
-
- assertEquals(1, config.getEventMetricCount());
- assertEquals(1, config.getGaugeMetricCount());
-
- final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
- assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId()));
- assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
-
- StatsdConfigProto.EventMetric eventMetric = config.getEventMetric(0);
- assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId()));
-
- final List<StatsdConfigProto.AtomMatcher> atomMatchers =
- new ArrayList<>(config.getAtomMatcherList());
-
- assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
- .getSimpleAtomMatcher().getAtomId());
- assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
- findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
- .getSimpleAtomMatcher().getAtomId());
-
- StatsdConfigProto.AtomMatcher unionMatcher = findAndRemoveAtomMatcherById(atomMatchers,
- eventMetric.getWhat());
- assertNotNull(unionMatcher.getCombination());
- assertEquals(2, unionMatcher.getCombination().getMatcherCount());
-
- Integer[] actualAtoms = new Integer[]{
- findAndRemoveAtomMatcherById(atomMatchers,
- unionMatcher.getCombination().getMatcher(0))
- .getSimpleAtomMatcher().getAtomId(),
- findAndRemoveAtomMatcherById(atomMatchers,
- unionMatcher.getCombination().getMatcher(1))
- .getSimpleAtomMatcher().getAtomId()};
- Arrays.sort(actualAtoms);
- assertArrayEquals(pushedAtoms, actualAtoms);
-
- assertEquals(0, atomMatchers.size());
- }
-}
diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java
deleted file mode 100644
index 363fac0c78ba..000000000000
--- a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2020 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.statsd.shelltools.testdrive;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Tests for {@link TestDrive}
- */
-@RunWith(Parameterized.class)
-public class TestDriveTest {
- /**
- * Expected results of a single iteration of the paramerized test.
- */
- static class Expect {
- public boolean success;
- public Integer[] atoms;
- public boolean onePushedAtomEvent = false;
- public String extraPackage = null;
- public String target;
- public boolean terse = false;
-
- static Expect success(Integer... atoms) {
- return new Expect(true, atoms,
- TARGET);
- }
- Expect(boolean success, Integer[] atoms, String target) {
- this.success = success;
- this.atoms = atoms;
- this.target = target;
- }
- static final Expect FAILURE = new Expect(false, null, null);
- Expect onePushedAtomEvent() {
- this.onePushedAtomEvent = true;
- return this;
- }
- Expect extraPackage() {
- this.extraPackage = TestDriveTest.PACKAGE;
- return this;
- }
- Expect terse() {
- this.terse = true;
- return this;
- }
- }
-
- @Parameterized.Parameter(0)
- public String[] mArgs;
-
- @Parameterized.Parameter(1)
- public List<String> mConnectedDevices;
-
- @Parameterized.Parameter(2)
- public String mDefaultDevice;
-
- @Parameterized.Parameter(3)
- public Expect mExpect;
-
- private static final String TARGET = "target";
- private static final List<String> TARGET_AND_OTHER = Arrays.asList("otherDevice",
- TARGET);
- private static final List<String> TWO_OTHER_DEVICES = Arrays.asList(
- "other1", "other2");
- private static final List<String> TARGET_ONLY = Collections.singletonList(TARGET);
- private static final List<String> NOT_TARGET = Collections.singletonList("other");
- private static final List<String> NO_DEVICES = Collections.emptyList();
- private static final String PACKAGE = "extraPackage";
-
- @Parameterized.Parameters
- public static Collection<Object[]> data() {
- return Arrays.asList(
- new Object[]{new String[]{}, null, null,
- Expect.FAILURE}, // Usage explanation
- new Object[]{new String[]{"244", "245"}, null, null,
- Expect.FAILURE}, // Failure looking up connected devices
- new Object[]{new String[]{"244", "245"}, NO_DEVICES, null,
- Expect.FAILURE}, // No connected devices
- new Object[]{new String[]{"-s", TARGET, "244", "245"}, NOT_TARGET, null,
- Expect.FAILURE}, // Wrong device connected
- new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, null,
- Expect.FAILURE}, // Wrong devices connected
- new Object[]{new String[]{"244", "245"}, TARGET_ONLY, null,
- Expect.success(244, 245)}, // If only one device connected, guess that one
- new Object[]{new String[]{"244", "not_an_atom"}, TARGET_ONLY, null,
- Expect.success(244)}, // Ignore non-atoms
- new Object[]{new String[]{"not_an_atom"}, TARGET_ONLY, null,
- Expect.FAILURE}, // Require at least one atom
- new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, TARGET,
- Expect.FAILURE}, // ANDROID_SERIAL specifies non-connected target
- new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, TARGET,
- Expect.success(244, 245)}, // ANDROID_SERIAL specifies a valid target
- new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, null,
- Expect.FAILURE}, // Two connected devices, no indication of which to use
- new Object[]{new String[]{"-one", "244", "245"}, TARGET_ONLY, null,
- Expect.success(244, 245).onePushedAtomEvent()},
- new Object[]{new String[]{"-terse", "-one", "244", "245"}, TARGET_ONLY, null,
- Expect.success(244, 245).onePushedAtomEvent().terse()},
- new Object[]{new String[]{"-one", "-terse", "244", "245"}, TARGET_ONLY, null,
- Expect.success(244, 245).onePushedAtomEvent().terse()},
- new Object[]{new String[]{"-p", PACKAGE, "244", "245"}, TARGET_ONLY, null,
- Expect.success(244, 245).extraPackage()},
- new Object[]{new String[]{"-p", PACKAGE, "-one", "244", "245"}, TARGET_ONLY, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
- new Object[]{new String[]{"-one", "-p", PACKAGE, "244", "245"}, TARGET_ONLY, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
- new Object[]{new String[]{"-s", TARGET, "-one", "-p", PACKAGE, "244", "245"},
- TARGET_AND_OTHER, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
- new Object[]{new String[]{"-one", "-s", TARGET, "-p", PACKAGE, "244", "245"},
- TARGET_AND_OTHER, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
- new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "244", "245"},
- TARGET_AND_OTHER, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
- new Object[]{new String[]{"-terse", "-one", "-p", PACKAGE, "-s", TARGET,
- "244", "245"},
- TARGET_AND_OTHER, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()},
- new Object[]{new String[]{"-one", "-terse", "-p", PACKAGE, "-s", TARGET,
- "244", "245"},
- TARGET_AND_OTHER, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()},
- new Object[]{new String[]{"-one", "-p", PACKAGE, "-terse", "-s", TARGET,
- "244", "245"},
- TARGET_AND_OTHER, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()},
- new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "-terse",
- "244", "245"},
- TARGET_AND_OTHER, null,
- Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}
- );
- }
-
- private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration();
- private final TestDrive mTestDrive = new TestDrive();
-
- private static Integer[] collectAtoms(TestDrive.Configuration configuration) {
- Integer[] result = new Integer[configuration.mPulledAtoms.size()
- + configuration.mPushedAtoms.size()];
- int result_index = 0;
- for (Integer atom : configuration.mPushedAtoms) {
- result[result_index++] = atom;
- }
- for (Integer atom : configuration.mPulledAtoms) {
- result[result_index++] = atom;
- }
- Arrays.sort(result);
- return result;
- }
-
- @Test
- public void testProcessArgs() {
- boolean result = mTestDrive.processArgs(mConfiguration, mArgs, mConnectedDevices,
- mDefaultDevice);
- if (mExpect.success) {
- assertTrue(result);
- assertArrayEquals(mExpect.atoms, collectAtoms(mConfiguration));
- assertEquals(mExpect.onePushedAtomEvent, mConfiguration.mOnePushedAtomEvent);
- assertEquals(mExpect.target, mTestDrive.mDeviceSerial);
- if (mExpect.terse) {
- assertEquals(TestDrive.TerseDumper.class, mTestDrive.mDumper.getClass());
- } else {
- assertEquals(TestDrive.BasicDumper.class, mTestDrive.mDumper.getClass());
- }
- } else {
- assertFalse(result);
- }
- }
-}
diff --git a/cmds/statsd/tools/localtools/testdrive_manifest.txt b/cmds/statsd/tools/localtools/testdrive_manifest.txt
deleted file mode 100644
index 625ebfa4312a..000000000000
--- a/cmds/statsd/tools/localtools/testdrive_manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-class: com.android.statsd.shelltools.testdrive.TestDrive
diff --git a/core/api/current.txt b/core/api/current.txt
index b61bc2fde0e9..8b350f5b7c06 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2883,6 +2883,7 @@ package android.accessibilityservice {
method protected void onServiceConnected();
method public void onSystemActionsChanged();
method public final boolean performGlobalAction(int);
+ method public void setAccessibilityFocusAppearance(int, @ColorInt int);
method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region);
@@ -7150,6 +7151,7 @@ package android.app.admin {
field @RequiresPermission(android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY";
field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
+ field public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES = "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
@@ -7254,6 +7256,7 @@ package android.app.admin {
field public static final int PRIVATE_DNS_SET_NO_ERROR = 0; // 0x0
field public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1; // 0x1
field public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2; // 0x2
+ field public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3; // 0x3
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -7686,6 +7689,7 @@ package android.app.job {
method public long getTriggerContentMaxDelay();
method public long getTriggerContentUpdateDelay();
method @Nullable public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
+ method public boolean isForegroundJob();
method public boolean isImportantWhileForeground();
method public boolean isPeriodic();
method public boolean isPersisted();
@@ -7717,7 +7721,8 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setClipData(@Nullable android.content.ClipData, int);
method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
- method public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
+ method @NonNull public android.app.job.JobInfo.Builder setForeground(boolean);
+ method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
@@ -7757,6 +7762,7 @@ package android.app.job {
method @NonNull public android.os.Bundle getTransientExtras();
method @Nullable public String[] getTriggeredContentAuthorities();
method @Nullable public android.net.Uri[] getTriggeredContentUris();
+ method public boolean isForegroundJob();
method public boolean isOverrideDeadlineExpired();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobParameters> CREATOR;
@@ -10328,7 +10334,7 @@ package android.content {
method public int checkPermission(String, int, int);
method public int checkSelfPermission(String);
method public int checkUriPermission(android.net.Uri, int, int, int);
- method public int checkUriPermission(android.net.Uri, String, String, int, int, int);
+ method public int checkUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int);
method @Deprecated public void clearWallpaper() throws java.io.IOException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
method public android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -10339,13 +10345,13 @@ package android.content {
method public boolean deleteDatabase(String);
method public boolean deleteFile(String);
method public boolean deleteSharedPreferences(String);
- method public void enforceCallingOrSelfPermission(String, String);
+ method public void enforceCallingOrSelfPermission(String, @Nullable String);
method public void enforceCallingOrSelfUriPermission(android.net.Uri, int, String);
- method public void enforceCallingPermission(String, String);
+ method public void enforceCallingPermission(String, @Nullable String);
method public void enforceCallingUriPermission(android.net.Uri, int, String);
- method public void enforcePermission(String, int, int, String);
+ method public void enforcePermission(String, int, int, @Nullable String);
method public void enforceUriPermission(android.net.Uri, int, int, int, String);
- method public void enforceUriPermission(android.net.Uri, String, String, int, int, int, String);
+ method public void enforceUriPermission(@Nullable android.net.Uri, @Nullable String, @Nullable String, int, int, int, @Nullable String);
method public String[] fileList();
method public android.content.Context getApplicationContext();
method public android.content.pm.ApplicationInfo getApplicationInfo();
@@ -10358,9 +10364,9 @@ package android.content {
method public java.io.File getDataDir();
method public java.io.File getDatabasePath(String);
method public java.io.File getDir(String, int);
- method public java.io.File getExternalCacheDir();
+ method @Nullable public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
- method public java.io.File getExternalFilesDir(String);
+ method @Nullable public java.io.File getExternalFilesDir(@Nullable String);
method public java.io.File[] getExternalFilesDirs(String);
method public java.io.File[] getExternalMediaDirs();
method public java.io.File getFileStreamPath(String);
@@ -10388,40 +10394,40 @@ package android.content {
method public java.io.FileInputStream openFileInput(String) throws java.io.FileNotFoundException;
method public java.io.FileOutputStream openFileOutput(String, int) throws java.io.FileNotFoundException;
method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
- method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, android.database.DatabaseErrorHandler);
+ method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler);
method @Deprecated public android.graphics.drawable.Drawable peekWallpaper();
- method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter);
- method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, int);
- method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler);
- method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler, int);
+ method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter);
+ method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, int);
+ method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler);
+ method public android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int);
method @Deprecated public void removeStickyBroadcast(android.content.Intent);
method @Deprecated public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
method public void revokeUriPermission(String, android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
- method public void sendBroadcast(android.content.Intent, String);
+ method public void sendBroadcast(android.content.Intent, @Nullable String);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, String);
- method public void sendOrderedBroadcast(android.content.Intent, String);
- method public void sendOrderedBroadcast(android.content.Intent, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
+ method public void sendOrderedBroadcast(android.content.Intent, @Nullable String);
+ method public void sendOrderedBroadcast(android.content.Intent, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method public void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, int, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, @Nullable String, @Nullable android.os.Bundle, @Nullable android.os.Bundle);
- method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
+ method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @Deprecated public void sendStickyBroadcast(android.content.Intent);
method @Deprecated public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
- method @Deprecated public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
- method @Deprecated public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
+ method @Deprecated public void sendStickyOrderedBroadcast(android.content.Intent, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
+ method @Deprecated public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method public void setTheme(int);
method @Deprecated public void setWallpaper(android.graphics.Bitmap) throws java.io.IOException;
method @Deprecated public void setWallpaper(java.io.InputStream) throws java.io.IOException;
method public void startActivities(android.content.Intent[]);
- method public void startActivities(android.content.Intent[], android.os.Bundle);
+ method public void startActivities(android.content.Intent[], @Nullable android.os.Bundle);
method public void startActivity(android.content.Intent);
- method public void startActivity(android.content.Intent, android.os.Bundle);
- method public android.content.ComponentName startForegroundService(android.content.Intent);
- method public boolean startInstrumentation(android.content.ComponentName, String, android.os.Bundle);
+ method public void startActivity(android.content.Intent, @Nullable android.os.Bundle);
+ method @Nullable public android.content.ComponentName startForegroundService(android.content.Intent);
+ method public boolean startInstrumentation(android.content.ComponentName, @Nullable String, @Nullable android.os.Bundle);
method public void startIntentSender(android.content.IntentSender, @Nullable android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, @Nullable android.content.Intent, int, int, int, @Nullable android.os.Bundle) throws android.content.IntentSender.SendIntentException;
- method public android.content.ComponentName startService(android.content.Intent);
+ method @Nullable public android.content.ComponentName startService(android.content.Intent);
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -38699,6 +38705,25 @@ package android.provider {
field public static final String UNGROUPED_WITH_PHONES = "summ_phones";
}
+ public static final class ContactsContract.SimAccount implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getAccountName();
+ method @NonNull public String getAccountType();
+ method public int getEfType();
+ method public int getSimSlotIndex();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ADN_EF_TYPE = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.provider.ContactsContract.SimAccount> CREATOR;
+ field public static final int FDN_EF_TYPE = 3; // 0x3
+ field public static final int SDN_EF_TYPE = 2; // 0x2
+ field public static final int UNKNOWN_EF_TYPE = 0; // 0x0
+ }
+
+ public static final class ContactsContract.SimContacts {
+ method @NonNull public static java.util.List<android.provider.ContactsContract.SimAccount> getSimAccounts(@NonNull android.content.ContentResolver);
+ field public static final String ACTION_SIM_ACCOUNTS_CHANGED = "android.provider.action.SIM_ACCOUNTS_CHANGED";
+ }
+
protected static interface ContactsContract.StatusColumns {
field public static final int AVAILABLE = 5; // 0x5
field public static final int AWAY = 2; // 0x2
@@ -42438,6 +42463,7 @@ package android.service.notification {
method public CharSequence getImportanceExplanation();
method public String getKey();
method public long getLastAudiblyAlertedMillis();
+ method public int getLockscreenVisibilityOverride();
method public String getOverrideGroupKey();
method public int getRank();
method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
@@ -42451,6 +42477,7 @@ package android.service.notification {
field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1
+ field public static final int VISIBILITY_NO_OVERRIDE = -1000; // 0xfffffc18
}
public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
@@ -44838,14 +44865,19 @@ package android.telecom {
field public static final String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME = "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME";
field public static final String EXTRA_DISCONNECT_CAUSE = "android.telecom.extra.DISCONNECT_CAUSE";
field public static final String EXTRA_HANDLE = "android.telecom.extra.HANDLE";
+ field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
+ field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
+ field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION";
field public static final String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
field public static final String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
+ field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
+ field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY";
field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
@@ -44861,6 +44893,8 @@ package android.telecom {
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+ field public static final int PRIORITY_NORMAL = 0; // 0x0
+ field public static final int PRIORITY_URGENT = 1; // 0x1
}
public class VideoProfile implements android.os.Parcelable {
@@ -45165,6 +45199,7 @@ package android.telephony {
field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool";
field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
+ field public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = "call_composer_picture_server_url_string";
field public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
field public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string";
field public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool";
@@ -45354,6 +45389,7 @@ package android.telephony {
field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+ field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool";
field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
@@ -46248,6 +46284,12 @@ package android.telephony {
field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1
}
+ public final class PhoneCapability implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR;
+ }
+
public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
ctor public PhoneNumberFormattingTextWatcher();
ctor public PhoneNumberFormattingTextWatcher(String);
@@ -46316,10 +46358,10 @@ package android.telephony {
public class PhoneStateListener {
ctor public PhoneStateListener();
- ctor public PhoneStateListener(@NonNull java.util.concurrent.Executor);
+ ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor);
method public void onActiveDataSubscriptionIdChanged(int);
method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
- method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
method public void onCallForwardingIndicatorChanged(boolean);
method public void onCallStateChanged(int, String);
method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
@@ -46327,36 +46369,140 @@ package android.telephony {
method public void onDataActivity(int);
method public void onDataConnectionStateChanged(int);
method public void onDataConnectionStateChanged(int, int);
- method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
- method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
method public void onMessageWaitingIndicatorChanged(boolean);
- method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
method public void onServiceStateChanged(android.telephony.ServiceState);
method @Deprecated public void onSignalStrengthChanged(int);
method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
method public void onUserMobileDataStateChanged(boolean);
- field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
- field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
- field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
- field public static final int LISTEN_CALL_STATE = 32; // 0x20
- field public static final int LISTEN_CELL_INFO = 1024; // 0x400
- field public static final int LISTEN_CELL_LOCATION = 16; // 0x10
- field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
- field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
- field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
- field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
- field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
- field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
+ field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
+ field @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
+ field @Deprecated public static final int LISTEN_CALL_STATE = 32; // 0x20
+ field @Deprecated public static final int LISTEN_CELL_INFO = 1024; // 0x400
+ field @Deprecated public static final int LISTEN_CELL_LOCATION = 16; // 0x10
+ field @Deprecated public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
+ field @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
+ field @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
+ field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
+ field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
field public static final int LISTEN_NONE = 0; // 0x0
- field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
- field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
+ field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1
field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
- field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
- field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
+ field @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
+ field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
+ }
+
+ public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
+ }
+
+ public static interface PhoneStateListener.AlwaysReportedSignalStrengthChangedListener {
+ method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+ }
+
+ public static interface PhoneStateListener.BarringInfoChangedListener {
+ method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
+ }
+
+ public static interface PhoneStateListener.CallDisconnectCauseChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
+ }
+
+ public static interface PhoneStateListener.CallForwardingIndicatorChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
+ }
+
+ public static interface PhoneStateListener.CallStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String);
+ }
+
+ public static interface PhoneStateListener.CarrierNetworkChangeListener {
+ method public void onCarrierNetworkChange(boolean);
+ }
+
+ public static interface PhoneStateListener.CellInfoChangedListener {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
+ }
+
+ public static interface PhoneStateListener.CellLocationChangedListener {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation);
+ }
+
+ public static interface PhoneStateListener.DataActivationStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
+ }
+
+ public static interface PhoneStateListener.DataActivityListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
+ }
+
+ public static interface PhoneStateListener.DataConnectionStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
+ }
+
+ public static interface PhoneStateListener.DisplayInfoChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+ }
+
+ public static interface PhoneStateListener.EmergencyNumberListChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
+ }
+
+ public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+ }
+
+ public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
+ }
+
+ public static interface PhoneStateListener.PhoneCapabilityChangedListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+ }
+
+ public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+ }
+
+ public static interface PhoneStateListener.RegistrationFailedListener {
+ method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+ }
+
+ public static interface PhoneStateListener.ServiceStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
+ }
+
+ public static interface PhoneStateListener.SignalStrengthsChangedListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+ }
+
+ public static interface PhoneStateListener.UserMobileDataStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
+ }
+
+ public final class PhysicalChannelConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCellBandwidthDownlink();
+ method public int getChannelNumber();
+ method public int getConnectionStatus();
+ method public int getNetworkType();
+ method @IntRange(from=0, to=1007) public int getPhysicalCellId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
+ field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
+ field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
+ field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
+ field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
}
public final class PreciseDataConnectionState implements android.os.Parcelable {
@@ -46868,7 +47014,8 @@ package android.telephony {
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
- method public void listen(long, @NonNull android.telephony.PhoneStateListener);
+ method @Deprecated public void listen(long, @NonNull android.telephony.PhoneStateListener);
+ method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(String);
@@ -46890,6 +47037,7 @@ package android.telephony {
method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
+ method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener);
method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
@@ -47671,6 +47819,7 @@ package android.telephony.ims.feature {
public static class MmTelFeature.MmTelCapabilities {
method public final boolean isCapable(int);
+ field public static final int CAPABILITY_TYPE_CALL_COMPOSER = 16; // 0x10
field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
@@ -50868,7 +51017,7 @@ package android.view {
method public int getFlags();
method @Nullable public android.net.Uri getLinkUri();
method public int getSource();
- method @NonNull public java.util.Map<java.lang.Boolean,android.view.ContentInfo> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>);
+ method @NonNull public android.util.Pair<android.view.ContentInfo,android.view.ContentInfo> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>);
field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
field public static final int SOURCE_APP = 0; // 0x0
field public static final int SOURCE_AUTOFILL = 4; // 0x4
@@ -54586,6 +54735,8 @@ package android.view.accessibility {
method public void addAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, @Nullable android.os.Handler);
method public boolean addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
method public void addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, @Nullable android.os.Handler);
+ method @ColorInt public int getAccessibilityFocusColor();
+ method public int getAccessibilityFocusStrokeWidth();
method @Deprecated public java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
@@ -56539,6 +56690,7 @@ package android.view.textclassifier {
method @Nullable public String getId();
method public int getSelectionEndIndex();
method public int getSelectionStartIndex();
+ method @Nullable public android.view.textclassifier.TextClassification getTextClassification();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextSelection> CREATOR;
}
@@ -56549,6 +56701,7 @@ package android.view.textclassifier {
method @NonNull public android.view.textclassifier.TextSelection.Builder setEntityType(@NonNull String, @FloatRange(from=0.0, to=1.0) float);
method @NonNull public android.view.textclassifier.TextSelection.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.view.textclassifier.TextSelection.Builder setId(@Nullable String);
+ method @NonNull public android.view.textclassifier.TextSelection.Builder setTextClassification(@Nullable android.view.textclassifier.TextClassification);
}
public static final class TextSelection.Request implements android.os.Parcelable {
@@ -56559,6 +56712,7 @@ package android.view.textclassifier {
method @NonNull public android.os.Bundle getExtras();
method @IntRange(from=0) public int getStartIndex();
method @NonNull public CharSequence getText();
+ method public boolean shouldIncludeTextClassification();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextSelection.Request> CREATOR;
}
@@ -56568,6 +56722,7 @@ package android.view.textclassifier {
method @NonNull public android.view.textclassifier.TextSelection.Request build();
method @NonNull public android.view.textclassifier.TextSelection.Request.Builder setDefaultLocales(@Nullable android.os.LocaleList);
method @NonNull public android.view.textclassifier.TextSelection.Request.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.textclassifier.TextSelection.Request.Builder setIncludeTextClassification(boolean);
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a5f2b6ac3af8..c3bf05813b90 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -878,11 +878,13 @@ package android.app.admin {
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
+ field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES";
field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER";
field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
- field public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
+ field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4
+ field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
@@ -891,6 +893,9 @@ package android.app.admin {
field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3
field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1
field public static final int STATE_USER_UNMANAGED = 0; // 0x0
+ field public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; // 0x3
+ field public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1
+ field public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2
}
public final class SystemUpdatePolicy implements android.os.Parcelable {
@@ -1795,11 +1800,11 @@ package android.content {
public class ContextWrapper extends android.content.Context {
method public android.content.Context createCredentialProtectedStorageContext();
- method public java.io.File getPreloadsFileCache();
+ method @Nullable public java.io.File getPreloadsFileCache();
method public boolean isCredentialProtectedStorage();
- method public void sendBroadcast(android.content.Intent, String, android.os.Bundle);
- method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.os.Bundle);
- method public void sendOrderedBroadcast(android.content.Intent, String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
+ method public void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
+ method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
+ method public void sendOrderedBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
}
public class Intent implements java.lang.Cloneable android.os.Parcelable {
@@ -8383,6 +8388,11 @@ package android.provider {
field @Deprecated public static final String STATE = "state";
}
+ public static final class ContactsContract.SimContacts {
+ method @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS") public static void addSimAccount(@NonNull android.content.ContentResolver, @NonNull String, @NonNull String, int, int);
+ method @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS") public static void removeSimAccounts(@NonNull android.content.ContentResolver, int);
+ }
+
public final class DeviceConfig {
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
@@ -8401,6 +8411,7 @@ package android.provider {
field public static final String NAMESPACE_APP_COMPAT = "app_compat";
field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
field public static final String NAMESPACE_AUTOFILL = "autofill";
+ field public static final String NAMESPACE_BATTERY_SAVER = "battery_saver";
field public static final String NAMESPACE_BIOMETRICS = "biometrics";
field public static final String NAMESPACE_BLOBSTORE = "blobstore";
field public static final String NAMESPACE_BLUETOOTH = "bluetooth";
@@ -9088,6 +9099,26 @@ package android.service.carrier {
method @NonNull @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
}
+ public final class CarrierMessagingServiceWrapper {
+ ctor public CarrierMessagingServiceWrapper();
+ method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull Runnable);
+ method public void disposeConnection(@NonNull android.content.Context);
+ method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
+ method public void receiveSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
+ method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
+ method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
+ method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
+ method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
+ }
+
+ public static interface CarrierMessagingServiceWrapper.CarrierMessagingCallback {
+ method public default void onDownloadMmsComplete(int);
+ method public default void onReceiveSmsComplete(int);
+ method public default void onSendMmsComplete(int, @Nullable byte[]);
+ method public default void onSendMultipartSmsComplete(int, @Nullable int[]);
+ method public default void onSendSmsComplete(int, int);
+ }
+
}
package android.service.contentcapture {
@@ -9708,6 +9739,33 @@ package android.telecom {
field @Deprecated public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
+ public final class BluetoothCallQualityReport implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public int getNegativeAcknowledgementCount();
+ method @IntRange(from=0) public int getPacketsNotReceivedCount();
+ method @IntRange(from=0) public int getRetransmittedPacketsCount();
+ method @IntRange(from=0xffffff81, to=20) public int getRssiDbm();
+ method public long getSentTimestampMillis();
+ method public int getSnrDb();
+ method public boolean isChoppyVoice();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telecom.BluetoothCallQualityReport> CREATOR;
+ field public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT";
+ field public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT";
+ }
+
+ public static final class BluetoothCallQualityReport.Builder {
+ ctor public BluetoothCallQualityReport.Builder();
+ method @NonNull public android.telecom.BluetoothCallQualityReport build();
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setChoppyVoice(boolean);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setNegativeAcknowledgementCount(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setPacketsNotReceivedCount(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRetransmittedPacketsCount(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRssiDbm(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSentTimestampMillis(long);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSnrDb(int);
+ }
+
public final class Call {
method @Deprecated public void addListener(android.telecom.Call.Listener);
method public void enterBackgroundAudioProcessing();
@@ -10375,35 +10433,87 @@ package android.telephony {
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
- method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
- field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
- field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 4294967296L; // 0x100000000L
- field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
- field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
- }
-
- public final class PhysicalChannelConfig implements android.os.Parcelable {
- method public int describeContents();
- method public int getCellBandwidthDownlink();
- method public int getChannelNumber();
- method public int getConnectionStatus();
- method public int getNetworkType();
- method @IntRange(from=0, to=1007) public int getPhysicalCellId();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff
- field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
- field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2
- field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR;
- field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
+ field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
+ field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
+ field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
+ field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
+ field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
+ field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
+ field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
+ field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe
+ field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22
+ field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c
+ field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e
+ field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc
+ field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18
+ field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f
+ field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
+ field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
+ field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10
+ field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
+ field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
+ }
+
+ public static interface PhoneStateListener.CallAttributesChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
+ }
+
+ public static interface PhoneStateListener.DataEnabledChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int);
+ }
+
+ public static interface PhoneStateListener.OutgoingEmergencyCallListener {
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
+ }
+
+ public static interface PhoneStateListener.OutgoingEmergencySmsListener {
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
+ }
+
+ public static interface PhoneStateListener.PhysicalChannelConfigChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
+ }
+
+ public static interface PhoneStateListener.PreciseCallStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
+ }
+
+ public static interface PhoneStateListener.RadioPowerStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
+ }
+
+ public static interface PhoneStateListener.SrvccStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
+ }
+
+ public static interface PhoneStateListener.VoiceActivationStateChangedListener {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
}
public final class PinResult implements android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2394919d4e1a..a448435926d9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -18,6 +18,7 @@ package android {
field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+ field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
@@ -750,9 +751,14 @@ package android.hardware.display {
}
public final class DisplayManager {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public int getRefreshRateSwitchingType();
method public boolean isMinimalPostProcessingRequested(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
+ field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
+ field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
+ field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
@@ -988,9 +994,9 @@ package android.media {
}
public class AudioSystem {
- method public static float getMasterBalance();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static float getMasterBalance();
method public static final int getNumStreamTypes();
- method public static int setMasterBalance(float);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static int setMasterBalance(float);
field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
field public static final int DEVICE_ROLE_NONE = 0; // 0x0
field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 0ad9e446dfc7..8e50184c96e0 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -18,6 +18,7 @@ package android.accessibilityservice;
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
import android.annotation.CallbackExecutor;
+import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -2118,6 +2119,27 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Sets the strokeWidth and color of the accessibility focus rectangle.
+ *
+ * @param strokeWidth The stroke width of the rectangle in pixels.
+ * Setting this value to zero results in no focus rectangle being drawn.
+ * @param color The color of the rectangle.
+ */
+ public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) {
+ IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
+ if (connection != null) {
+ try {
+ connection.setFocusAppearance(strokeWidth, color);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting the strokeWidth and color of the "
+ + "accessibility focus rectangle", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Implement to return the implementation of the internal accessibility
* service interface.
*/
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 0b3b9b2ecae1..ab21dc9f14ad 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -115,4 +115,6 @@ interface IAccessibilityServiceConnection {
void setGestureDetectionPassthroughRegion(int displayId, in Region region);
void setTouchExplorationPassthroughRegion(int displayId, in Region region);
+
+ void setFocusAppearance(int strokeWidth, int color);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5ee5597e1984..f92768a28db1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -963,8 +963,8 @@ public class Activity extends ContextThemeWrapper
* @hide
*/
@Override
- public void toggleFreeformWindowingMode() throws RemoteException {
- ActivityTaskManager.getService().toggleFreeformWindowingMode(mToken);
+ public void toggleFreeformWindowingMode() {
+ ActivityClient.getInstance().toggleFreeformWindowingMode(mToken);
}
/**
@@ -981,11 +981,8 @@ public class Activity extends ContextThemeWrapper
@Override
public boolean isTaskRoot() {
- try {
- return ActivityTaskManager.getService().getTaskForActivity(mToken, true) >= 0;
- } catch (RemoteException e) {
- return false;
- }
+ return ActivityClient.getInstance().getTaskForActivity(
+ mToken, true /* onlyRoot */) >= 0;
}
/**
@@ -2052,12 +2049,8 @@ public class Activity extends ContextThemeWrapper
* {@link #isVoiceInteractionRoot()} return {@code false} in this case.
*/
public boolean isVoiceInteractionRoot() {
- try {
- return mVoiceInteractor != null
- && ActivityTaskManager.getService().isRootVoiceInteraction(mToken);
- } catch (RemoteException e) {
- }
- return false;
+ return mVoiceInteractor != null
+ && ActivityClient.getInstance().isRootVoiceInteraction(mToken);
}
/**
@@ -2090,10 +2083,7 @@ public class Activity extends ContextThemeWrapper
* @param privateOptions a Bundle of private arguments to the current voice interaction service
*/
public void startLocalVoiceInteraction(Bundle privateOptions) {
- try {
- ActivityTaskManager.getService().startLocalVoiceInteraction(mToken, privateOptions);
- } catch (RemoteException re) {
- }
+ ActivityClient.getInstance().startLocalVoiceInteraction(mToken, privateOptions);
}
/**
@@ -2119,10 +2109,7 @@ public class Activity extends ContextThemeWrapper
* terminated, {@link #onLocalVoiceInteractionStopped()} will be called.
*/
public void stopLocalVoiceInteraction() {
- try {
- ActivityTaskManager.getService().stopLocalVoiceInteraction(mToken);
- } catch (RemoteException re) {
- }
+ ActivityClient.getInstance().stopLocalVoiceInteraction(mToken);
}
/**
@@ -2564,11 +2551,7 @@ public class Activity extends ContextThemeWrapper
* false will be returned if the caller is not the current top activity.
*/
public boolean showAssist(Bundle args) {
- try {
- return ActivityTaskManager.getService().showAssistFromActivity(mToken, args);
- } catch (RemoteException e) {
- }
- return false;
+ return ActivityClient.getInstance().showAssistFromActivity(mToken, args);
}
/**
@@ -2708,10 +2691,9 @@ public class Activity extends ContextThemeWrapper
}
mDoReportFullyDrawn = false;
try {
- ActivityTaskManager.getService().reportActivityFullyDrawn(
+ ActivityClient.getInstance().reportActivityFullyDrawn(
mToken, mRestoredFromBundle);
VMRuntime.getRuntime().notifyStartupCompleted();
- } catch (RemoteException e) {
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -2841,27 +2823,23 @@ public class Activity extends ContextThemeWrapper
* does not support picture-in-picture, return false.
*/
public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
- try {
- if (!deviceSupportsPictureInPictureMode()) {
- return false;
- }
- if (params == null) {
- throw new IllegalArgumentException("Expected non-null picture-in-picture params");
- }
- if (!mCanEnterPictureInPicture) {
- throw new IllegalStateException("Activity must be resumed to enter"
- + " picture-in-picture");
- }
- // Set mIsInPictureInPictureMode earlier and don't wait for
- // onPictureInPictureModeChanged callback here. This is to ensure that
- // isInPictureInPictureMode returns true in the following onPause callback.
- // See https://developer.android.com/guide/topics/ui/picture-in-picture for guidance.
- mIsInPictureInPictureMode = ActivityTaskManager.getService().enterPictureInPictureMode(
- mToken, params);
- return mIsInPictureInPictureMode;
- } catch (RemoteException e) {
+ if (!deviceSupportsPictureInPictureMode()) {
return false;
}
+ if (params == null) {
+ throw new IllegalArgumentException("Expected non-null picture-in-picture params");
+ }
+ if (!mCanEnterPictureInPicture) {
+ throw new IllegalStateException("Activity must be resumed to enter"
+ + " picture-in-picture");
+ }
+ // Set mIsInPictureInPictureMode earlier and don't wait for
+ // onPictureInPictureModeChanged callback here. This is to ensure that
+ // isInPictureInPictureMode returns true in the following onPause callback.
+ // See https://developer.android.com/guide/topics/ui/picture-in-picture for guidance.
+ mIsInPictureInPictureMode = ActivityClient.getInstance().enterPictureInPictureMode(
+ mToken, params);
+ return mIsInPictureInPictureMode;
}
/**
@@ -2871,16 +2849,13 @@ public class Activity extends ContextThemeWrapper
* @param params the new parameters for the picture-in-picture.
*/
public void setPictureInPictureParams(@NonNull PictureInPictureParams params) {
- try {
- if (!deviceSupportsPictureInPictureMode()) {
- return;
- }
- if (params == null) {
- throw new IllegalArgumentException("Expected non-null picture-in-picture params");
- }
- ActivityTaskManager.getService().setPictureInPictureParams(mToken, params);
- } catch (RemoteException e) {
+ if (!deviceSupportsPictureInPictureMode()) {
+ return;
}
+ if (params == null) {
+ throw new IllegalArgumentException("Expected non-null picture-in-picture params");
+ }
+ ActivityClient.getInstance().setPictureInPictureParams(mToken, params);
}
/**
@@ -3822,14 +3797,10 @@ public class Activity extends ContextThemeWrapper
finishAfterTransition();
return;
}
- try {
- // Inform activity task manager that the activity received a back press
- // while at the root of the task. This call allows ActivityTaskManager
- // to intercept or move the task to the back.
- ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken);
- } catch (RemoteException e) {
- finishAfterTransition();
- }
+ // Inform activity task manager that the activity received a back press while at the
+ // root of the task. This call allows ActivityTaskManager to intercept or move the task
+ // to the back.
+ ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken);
// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
// be restored now.
@@ -6114,11 +6085,8 @@ public class Activity extends ContextThemeWrapper
* the outgoing activity. Use 0 for no animation.
*/
public void overridePendingTransition(int enterAnim, int exitAnim) {
- try {
- ActivityTaskManager.getService().overridePendingTransition(
- mToken, getPackageName(), enterAnim, exitAnim);
- } catch (RemoteException e) {
- }
+ ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(),
+ enterAnim, exitAnim);
}
/**
@@ -6239,11 +6207,7 @@ public class Activity extends ContextThemeWrapper
*/
@Nullable
public String getCallingPackage() {
- try {
- return ActivityTaskManager.getService().getCallingPackage(mToken);
- } catch (RemoteException e) {
- return null;
- }
+ return ActivityClient.getInstance().getCallingPackage(mToken);
}
/**
@@ -6262,11 +6226,7 @@ public class Activity extends ContextThemeWrapper
*/
@Nullable
public ComponentName getCallingActivity() {
- try {
- return ActivityTaskManager.getService().getCallingActivity(mToken);
- } catch (RemoteException e) {
- return null;
- }
+ return ActivityClient.getInstance().getCallingActivity(mToken);
}
/**
@@ -6364,16 +6324,12 @@ public class Activity extends ContextThemeWrapper
resultData = mResultData;
}
if (false) Log.v(TAG, "Finishing self: token=" + mToken);
- try {
- if (resultData != null) {
- resultData.prepareToLeaveProcess(this);
- }
- if (ActivityTaskManager.getService()
- .finishActivity(mToken, resultCode, resultData, finishTask)) {
- mFinished = true;
- }
- } catch (RemoteException e) {
- // Empty
+ if (resultData != null) {
+ resultData.prepareToLeaveProcess(this);
+ }
+ if (ActivityClient.getInstance().finishActivity(mToken, resultCode, resultData,
+ finishTask)) {
+ mFinished = true;
}
} else {
mParent.finishFromChild(this);
@@ -6429,12 +6385,8 @@ public class Activity extends ContextThemeWrapper
if (mResultCode != RESULT_CANCELED || mResultData != null) {
throw new IllegalStateException("Can not be called to deliver a result");
}
- try {
- if (ActivityTaskManager.getService().finishActivityAffinity(mToken)) {
- mFinished = true;
- }
- } catch (RemoteException e) {
- // Empty
+ if (ActivityClient.getInstance().finishActivityAffinity(mToken)) {
+ mFinished = true;
}
}
@@ -6477,12 +6429,7 @@ public class Activity extends ContextThemeWrapper
*/
public void finishActivity(int requestCode) {
if (mParent == null) {
- try {
- ActivityTaskManager.getService()
- .finishSubActivity(mToken, mEmbeddedID, requestCode);
- } catch (RemoteException e) {
- // Empty
- }
+ ActivityClient.getInstance().finishSubActivity(mToken, mEmbeddedID, requestCode);
} else {
mParent.finishActivityFromChild(this, requestCode);
}
@@ -6499,12 +6446,7 @@ public class Activity extends ContextThemeWrapper
*/
@Deprecated
public void finishActivityFromChild(@NonNull Activity child, int requestCode) {
- try {
- ActivityTaskManager.getService()
- .finishSubActivity(mToken, child.mEmbeddedID, requestCode);
- } catch (RemoteException e) {
- // Empty
- }
+ ActivityClient.getInstance().finishSubActivity(mToken, child.mEmbeddedID, requestCode);
}
/**
@@ -6527,12 +6469,7 @@ public class Activity extends ContextThemeWrapper
* being finished, it hasn't yet saved its state, etc.
*/
public boolean releaseInstance() {
- try {
- return ActivityTaskManager.getService().releaseActivityInstance(mToken);
- } catch (RemoteException e) {
- // Empty
- }
- return false;
+ return ActivityClient.getInstance().releaseActivityInstance(mToken);
}
/**
@@ -6644,12 +6581,7 @@ public class Activity extends ContextThemeWrapper
*/
public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
if (mParent == null) {
- try {
- ActivityTaskManager.getService().setRequestedOrientation(
- mToken, requestedOrientation);
- } catch (RemoteException e) {
- // Empty
- }
+ ActivityClient.getInstance().setRequestedOrientation(mToken, requestedOrientation);
} else {
mParent.setRequestedOrientation(requestedOrientation);
}
@@ -6667,16 +6599,10 @@ public class Activity extends ContextThemeWrapper
@ActivityInfo.ScreenOrientation
public int getRequestedOrientation() {
if (mParent == null) {
- try {
- return ActivityTaskManager.getService()
- .getRequestedOrientation(mToken);
- } catch (RemoteException e) {
- // Empty
- }
+ return ActivityClient.getInstance().getRequestedOrientation(mToken);
} else {
return mParent.getRequestedOrientation();
}
- return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
/**
@@ -6686,11 +6612,7 @@ public class Activity extends ContextThemeWrapper
* @return Task identifier, an opaque integer.
*/
public int getTaskId() {
- try {
- return ActivityTaskManager.getService().getTaskForActivity(mToken, false);
- } catch (RemoteException e) {
- return -1;
- }
+ return ActivityClient.getInstance().getTaskForActivity(mToken, false /* onlyRoot */);
}
/**
@@ -6715,12 +6637,7 @@ public class Activity extends ContextThemeWrapper
* back) true is returned, else false.
*/
public boolean moveTaskToBack(boolean nonRoot) {
- try {
- return ActivityTaskManager.getService().moveActivityTaskToBack(mToken, nonRoot);
- } catch (RemoteException e) {
- // Empty
- }
- return false;
+ return ActivityClient.getInstance().moveActivityTaskToBack(mToken, nonRoot);
}
/**
@@ -6896,10 +6813,7 @@ public class Activity extends ContextThemeWrapper
mTaskDescription.setIcon(Icon.createWithBitmap(icon));
}
}
- try {
- ActivityTaskManager.getService().setTaskDescription(mToken, mTaskDescription);
- } catch (RemoteException e) {
- }
+ ActivityClient.getInstance().setTaskDescription(mToken, mTaskDescription);
}
/**
@@ -7210,11 +7124,7 @@ public class Activity extends ContextThemeWrapper
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
*/
public boolean isImmersive() {
- try {
- return ActivityTaskManager.getService().isImmersive(mToken);
- } catch (RemoteException e) {
- return false;
- }
+ return ActivityClient.getInstance().isImmersive(mToken);
}
/**
@@ -7228,11 +7138,7 @@ public class Activity extends ContextThemeWrapper
if (mToken == null || mWindow == null) {
return false;
}
- try {
- return ActivityTaskManager.getService().isTopOfTask(getActivityToken());
- } catch (RemoteException e) {
- return false;
- }
+ return ActivityClient.getInstance().isTopOfTask(getActivityToken());
}
/**
@@ -7271,14 +7177,10 @@ public class Activity extends ContextThemeWrapper
}
private boolean convertFromTranslucentInternal() {
- try {
- mTranslucentCallback = null;
- if (ActivityTaskManager.getService().convertFromTranslucent(mToken)) {
- WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, true);
- return true;
- }
- } catch (RemoteException e) {
- // pass
+ mTranslucentCallback = null;
+ if (ActivityClient.getInstance().convertFromTranslucent(mToken)) {
+ WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, true /* opaque */);
+ return true;
}
return false;
}
@@ -7307,21 +7209,14 @@ public class Activity extends ContextThemeWrapper
@SystemApi
public boolean convertToTranslucent(TranslucentConversionListener callback,
ActivityOptions options) {
- boolean drawComplete;
- try {
- mTranslucentCallback = callback;
- mChangeCanvasToTranslucent = ActivityTaskManager.getService().convertToTranslucent(
- mToken, options == null ? null : options.toBundle());
- WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, false);
- drawComplete = true;
- } catch (RemoteException e) {
- // Make callback return as though it timed out.
- mChangeCanvasToTranslucent = false;
- drawComplete = false;
- }
+ mTranslucentCallback = callback;
+ mChangeCanvasToTranslucent = ActivityClient.getInstance().convertToTranslucent(
+ mToken, options == null ? null : options.toBundle());
+ WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, false);
+
if (!mChangeCanvasToTranslucent && mTranslucentCallback != null) {
// Window is already translucent.
- mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
+ mTranslucentCallback.onTranslucentConversionComplete(true /* drawComplete */);
}
return mChangeCanvasToTranslucent;
}
@@ -7355,12 +7250,7 @@ public class Activity extends ContextThemeWrapper
*/
@UnsupportedAppUsage
ActivityOptions getActivityOptions() {
- try {
- return ActivityOptions.fromBundle(
- ActivityTaskManager.getService().getActivityOptions(mToken));
- } catch (RemoteException e) {
- }
- return null;
+ return ActivityOptions.fromBundle(ActivityClient.getInstance().getActivityOptions(mToken));
}
/**
@@ -7504,11 +7394,7 @@ public class Activity extends ContextThemeWrapper
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
*/
public void setImmersive(boolean i) {
- try {
- ActivityTaskManager.getService().setImmersive(mToken, i);
- } catch (RemoteException e) {
- // pass
- }
+ ActivityClient.getInstance().setImmersive(mToken, i);
}
/**
@@ -7567,14 +7453,8 @@ public class Activity extends ContextThemeWrapper
*/
public void setVrModeEnabled(boolean enabled, @NonNull ComponentName requestedComponent)
throws PackageManager.NameNotFoundException {
- try {
- if (ActivityTaskManager.getService().setVrMode(mToken, enabled, requestedComponent)
- != 0) {
- throw new PackageManager.NameNotFoundException(
- requestedComponent.flattenToString());
- }
- } catch (RemoteException e) {
- // pass
+ if (ActivityClient.getInstance().setVrMode(mToken, enabled, requestedComponent) != 0) {
+ throw new PackageManager.NameNotFoundException(requestedComponent.flattenToString());
}
}
@@ -7689,9 +7569,7 @@ public class Activity extends ContextThemeWrapper
if (info.taskAffinity == null) {
return false;
}
- return ActivityTaskManager.getService().shouldUpRecreateTask(mToken, info.taskAffinity);
- } catch (RemoteException e) {
- return false;
+ return ActivityClient.getInstance().shouldUpRecreateTask(mToken, info.taskAffinity);
} catch (NameNotFoundException e) {
return false;
}
@@ -7739,13 +7617,9 @@ public class Activity extends ContextThemeWrapper
if (resultData != null) {
resultData.prepareToLeaveProcess(this);
}
- try {
- upIntent.prepareToLeaveProcess(this);
- return ActivityTaskManager.getService().navigateUpTo(mToken, upIntent,
- resultCode, resultData);
- } catch (RemoteException e) {
- return false;
- }
+ upIntent.prepareToLeaveProcess(this);
+ return ActivityClient.getInstance().navigateUpTo(mToken, upIntent, resultCode,
+ resultData);
} else {
return mParent.navigateUpToFromChild(this, upIntent);
}
@@ -8361,10 +8235,7 @@ public class Activity extends ContextThemeWrapper
* @see android.R.attr#lockTaskMode
*/
public void startLockTask() {
- try {
- ActivityTaskManager.getService().startLockTaskModeByToken(mToken);
- } catch (RemoteException e) {
- }
+ ActivityClient.getInstance().startLockTaskModeByToken(mToken);
}
/**
@@ -8384,10 +8255,7 @@ public class Activity extends ContextThemeWrapper
* @see ActivityManager#getLockTaskModeState()
*/
public void stopLockTask() {
- try {
- ActivityTaskManager.getService().stopLockTaskModeByToken(mToken);
- } catch (RemoteException e) {
- }
+ ActivityClient.getInstance().stopLockTaskModeByToken(mToken);
}
/**
@@ -8396,10 +8264,7 @@ public class Activity extends ContextThemeWrapper
* of this call for the message to be displayed.
*/
public void showLockTaskEscapeMessage() {
- try {
- ActivityTaskManager.getService().showLockTaskEscapeMessage(mToken);
- } catch (RemoteException e) {
- }
+ ActivityClient.getInstance().showLockTaskEscapeMessage(mToken);
}
/**
@@ -8667,11 +8532,7 @@ public class Activity extends ContextThemeWrapper
*/
@UnsupportedAppUsage
public void setDisablePreviewScreenshots(boolean disable) {
- try {
- ActivityTaskManager.getService().setDisablePreviewScreenshots(mToken, disable);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().setDisablePreviewScreenshots(mToken, disable);
}
/**
@@ -8688,11 +8549,7 @@ public class Activity extends ContextThemeWrapper
* @see android.R.attr#showWhenLocked
*/
public void setShowWhenLocked(boolean showWhenLocked) {
- try {
- ActivityTaskManager.getService().setShowWhenLocked(mToken, showWhenLocked);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().setShowWhenLocked(mToken, showWhenLocked);
}
/**
@@ -8711,12 +8568,7 @@ public class Activity extends ContextThemeWrapper
* @see android.R.attr#inheritShowWhenLocked
*/
public void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
- try {
- ActivityTaskManager.getService().setInheritShowWhenLocked(
- mToken, inheritShowWhenLocked);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().setInheritShowWhenLocked(mToken, inheritShowWhenLocked);
}
/**
@@ -8741,11 +8593,7 @@ public class Activity extends ContextThemeWrapper
* @see KeyguardManager#isDeviceSecure()
*/
public void setTurnScreenOn(boolean turnScreenOn) {
- try {
- ActivityTaskManager.getService().setTurnScreenOn(mToken, turnScreenOn);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().setTurnScreenOn(mToken, turnScreenOn);
}
/**
@@ -8757,11 +8605,7 @@ public class Activity extends ContextThemeWrapper
*/
@RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
- try {
- ActivityTaskManager.getService().registerRemoteAnimations(mToken, definition);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().registerRemoteAnimations(mToken, definition);
}
/**
@@ -8771,11 +8615,7 @@ public class Activity extends ContextThemeWrapper
*/
@RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
public void unregisterRemoteAnimations() {
- try {
- ActivityTaskManager.getService().unregisterRemoteAnimations(mToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().unregisterRemoteAnimations(mToken);
}
class HostCallbacks extends FragmentHostCallback<Activity> {
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
new file mode 100644
index 000000000000..84ecd24b8c55
--- /dev/null
+++ b/core/java/android/app/ActivityClient.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.util.Singleton;
+import android.view.RemoteAnimationDefinition;
+
+/**
+ * Provides the activity associated operations that communicate with system.
+ *
+ * @hide
+ */
+public class ActivityClient {
+ private ActivityClient() {}
+
+ /** Reports the main thread is idle after the activity is resumed. */
+ public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
+ try {
+ getActivityClientController().activityIdle(token, config, stopProfiling);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Reports {@link Activity#onResume()} is done. */
+ public void activityResumed(IBinder token) {
+ try {
+ getActivityClientController().activityResumed(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Reports after {@link Activity#onTopResumedActivityChanged(boolean)} is called for losing the
+ * top most position.
+ */
+ public void activityTopResumedStateLost() {
+ try {
+ getActivityClientController().activityTopResumedStateLost();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Reports {@link Activity#onPause()} is done. */
+ public void activityPaused(IBinder token) {
+ try {
+ getActivityClientController().activityPaused(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Reports {@link Activity#onStop()} is done. */
+ public void activityStopped(IBinder token, Bundle state, PersistableBundle persistentState,
+ CharSequence description) {
+ try {
+ getActivityClientController().activityStopped(token, state, persistentState,
+ description);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Reports {@link Activity#onDestroy()} is done. */
+ public void activityDestroyed(IBinder token) {
+ try {
+ getActivityClientController().activityDestroyed(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Reports the activity has completed relaunched. */
+ public void activityRelaunched(IBinder token) {
+ try {
+ getActivityClientController().activityRelaunched(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
+ int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
+ try {
+ getActivityClientController().reportSizeConfigurations(token,
+ horizontalSizeConfiguration, verticalSizeConfigurations,
+ smallestSizeConfigurations);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
+ try {
+ return getActivityClientController().moveActivityTaskToBack(token, nonRoot);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
+ try {
+ return getActivityClientController().shouldUpRecreateTask(token, destAffinity);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+ Intent resultData) {
+ try {
+ return getActivityClientController().navigateUpTo(token, destIntent, resultCode,
+ resultData);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean releaseActivityInstance(IBinder token) {
+ try {
+ return getActivityClientController().releaseActivityInstance(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ public boolean finishActivity(IBinder token, int resultCode, Intent resultData,
+ int finishTask) {
+ try {
+ return getActivityClientController().finishActivity(token, resultCode, resultData,
+ finishTask);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean finishActivityAffinity(IBinder token) {
+ try {
+ return getActivityClientController().finishActivityAffinity(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void finishSubActivity(IBinder token, String resultWho, int requestCode) {
+ try {
+ getActivityClientController().finishSubActivity(token, resultWho, requestCode);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ public boolean isTopOfTask(IBinder token) {
+ try {
+ return getActivityClientController().isTopOfTask(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean willActivityBeVisible(IBinder token) {
+ try {
+ return getActivityClientController().willActivityBeVisible(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ public int getDisplayId(IBinder token) {
+ try {
+ return getActivityClientController().getDisplayId(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ public int getTaskForActivity(IBinder token, boolean onlyRoot) {
+ try {
+ return getActivityClientController().getTaskForActivity(token, onlyRoot);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ ComponentName getCallingActivity(IBinder token) {
+ try {
+ return getActivityClientController().getCallingActivity(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ String getCallingPackage(IBinder token) {
+ try {
+ return getActivityClientController().getCallingPackage(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ Bundle getActivityOptions(IBinder token) {
+ try {
+ return getActivityClientController().getActivityOptions(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ public void setRequestedOrientation(IBinder token, int requestedOrientation) {
+ try {
+ getActivityClientController().setRequestedOrientation(token, requestedOrientation);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ int getRequestedOrientation(IBinder token) {
+ try {
+ return getActivityClientController().getRequestedOrientation(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean convertFromTranslucent(IBinder token) {
+ try {
+ return getActivityClientController().convertFromTranslucent(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean convertToTranslucent(IBinder token, Bundle options) {
+ try {
+ return getActivityClientController().convertToTranslucent(token, options);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
+ try {
+ getActivityClientController().reportActivityFullyDrawn(token, restoredFromBundle);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean isImmersive(IBinder token) {
+ try {
+ return getActivityClientController().isImmersive(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void setImmersive(IBinder token, boolean immersive) {
+ try {
+ getActivityClientController().setImmersive(token, immersive);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean enterPictureInPictureMode(IBinder token, PictureInPictureParams params) {
+ try {
+ return getActivityClientController().enterPictureInPictureMode(token, params);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void setPictureInPictureParams(IBinder token, PictureInPictureParams params) {
+ try {
+ getActivityClientController().setPictureInPictureParams(token, params);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void toggleFreeformWindowingMode(IBinder token) {
+ try {
+ getActivityClientController().toggleFreeformWindowingMode(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void startLockTaskModeByToken(IBinder token) {
+ try {
+ getActivityClientController().startLockTaskModeByToken(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void stopLockTaskModeByToken(IBinder token) {
+ try {
+ getActivityClientController().stopLockTaskModeByToken(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void showLockTaskEscapeMessage(IBinder token) {
+ try {
+ getActivityClientController().showLockTaskEscapeMessage(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
+ try {
+ getActivityClientController().setTaskDescription(token, td);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean showAssistFromActivity(IBinder token, Bundle args) {
+ try {
+ return getActivityClientController().showAssistFromActivity(token, args);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ boolean isRootVoiceInteraction(IBinder token) {
+ try {
+ return getActivityClientController().isRootVoiceInteraction(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+ try {
+ getActivityClientController().startLocalVoiceInteraction(callingActivity, options);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void stopLocalVoiceInteraction(IBinder callingActivity) {
+ try {
+ getActivityClientController().stopLocalVoiceInteraction(callingActivity);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void setShowWhenLocked(IBinder token, boolean showWhenLocked) {
+ try {
+ getActivityClientController().setShowWhenLocked(token, showWhenLocked);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) {
+ try {
+ getActivityClientController().setInheritShowWhenLocked(token, inheritShowWhenLocked);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
+ try {
+ getActivityClientController().setTurnScreenOn(token, turnScreenOn);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
+ try {
+ return getActivityClientController().setVrMode(token, enabled, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void overridePendingTransition(IBinder token, String packageName,
+ int enterAnim, int exitAnim) {
+ try {
+ getActivityClientController().overridePendingTransition(token, packageName,
+ enterAnim, exitAnim);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void setDisablePreviewScreenshots(IBinder token, boolean disable) {
+ try {
+ getActivityClientController().setDisablePreviewScreenshots(token, disable);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
+ try {
+ getActivityClientController().registerRemoteAnimations(token, definition);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void unregisterRemoteAnimations(IBinder token) {
+ try {
+ getActivityClientController().unregisterRemoteAnimations(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void onBackPressedOnTaskRoot(IBinder token) {
+ try {
+ getActivityClientController().onBackPressedOnTaskRoot(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ public static ActivityClient getInstance() {
+ return sInstance.get();
+ }
+
+ private static IActivityClientController getActivityClientController() {
+ return sActivityClientController.get();
+ }
+
+ private static final Singleton<ActivityClient> sInstance = new Singleton<ActivityClient>() {
+ @Override
+ protected ActivityClient create() {
+ return new ActivityClient();
+ }
+ };
+
+ private static final Singleton<IActivityClientController> sActivityClientController =
+ new Singleton<IActivityClientController>() {
+ @Override
+ protected IActivityClientController create() {
+ try {
+ return ActivityTaskManager.getService().getActivityClientController();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ };
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e3048dff1bda..38a22d8ade94 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -77,6 +77,8 @@ import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Singleton;
import android.util.Size;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.view.Surface;
import android.view.WindowInsetsController.Appearance;
@@ -1502,57 +1504,54 @@ public class ActivityManager {
}
/** @hide */
- public void saveToXml(XmlSerializer out) throws IOException {
+ public void saveToXml(TypedXmlSerializer out) throws IOException {
if (mLabel != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONLABEL, mLabel);
}
if (mColorPrimary != 0) {
- out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY,
- Integer.toHexString(mColorPrimary));
+ out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, mColorPrimary);
}
if (mColorBackground != 0) {
- out.attribute(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND,
- Integer.toHexString(mColorBackground));
+ out.attributeIntHex(null, ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, mColorBackground);
}
if (mIconFilename != null) {
out.attribute(null, ATTR_TASKDESCRIPTIONICON_FILENAME, mIconFilename);
}
if (mIcon != null && mIcon.getType() == Icon.TYPE_RESOURCE) {
- out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE,
- Integer.toString(mIcon.getResId()));
+ out.attributeInt(null, ATTR_TASKDESCRIPTIONICON_RESOURCE, mIcon.getResId());
out.attribute(null, ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE,
mIcon.getResPackage());
}
}
/** @hide */
- public void restoreFromXml(XmlPullParser in) {
+ public void restoreFromXml(TypedXmlPullParser in) {
final String label = in.getAttributeValue(null, ATTR_TASKDESCRIPTIONLABEL);
if (label != null) {
setLabel(label);
}
- final String colorPrimary = in.getAttributeValue(null,
- ATTR_TASKDESCRIPTIONCOLOR_PRIMARY);
- if (colorPrimary != null) {
- setPrimaryColor((int) Long.parseLong(colorPrimary, 16));
+ final int colorPrimary = in.getAttributeIntHex(null,
+ ATTR_TASKDESCRIPTIONCOLOR_PRIMARY, 0);
+ if (colorPrimary != 0) {
+ setPrimaryColor(colorPrimary);
}
- final String colorBackground = in.getAttributeValue(null,
- ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND);
- if (colorBackground != null) {
- setBackgroundColor((int) Long.parseLong(colorBackground, 16));
+ final int colorBackground = in.getAttributeIntHex(null,
+ ATTR_TASKDESCRIPTIONCOLOR_BACKGROUND, 0);
+ if (colorBackground != 0) {
+ setBackgroundColor(colorBackground);
}
final String iconFilename = in.getAttributeValue(null,
ATTR_TASKDESCRIPTIONICON_FILENAME);
if (iconFilename != null) {
setIconFilename(iconFilename);
}
- final String iconResourceId = in.getAttributeValue(null,
- ATTR_TASKDESCRIPTIONICON_RESOURCE);
+ final int iconResourceId = in.getAttributeInt(null,
+ ATTR_TASKDESCRIPTIONICON_RESOURCE, Resources.ID_NULL);
final String iconResourcePackage = in.getAttributeValue(null,
ATTR_TASKDESCRIPTIONICON_RESOURCE_PACKAGE);
- if (iconResourceId != null && iconResourcePackage != null) {
+ if (iconResourceId != Resources.ID_NULL && iconResourcePackage != null) {
setIcon(Icon.createWithResource(iconResourcePackage,
- Integer.parseInt(iconResourceId, 10)));
+ iconResourceId));
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d9c0c71d44ae..ed6dea815e4c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -85,6 +85,7 @@ import android.database.sqlite.SQLiteDebug.DbStats;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
+import android.graphics.Rect;
import android.graphics.Typeface;
import android.hardware.display.DisplayManagerGlobal;
import android.inputmethodservice.InputMethodService;
@@ -2154,7 +2155,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
if (a != null) {
mNewActivities = null;
- IActivityTaskManager am = ActivityTaskManager.getService();
+ final ActivityClient ac = ActivityClient.getInstance();
ActivityClientRecord prev;
do {
if (localLOGV) Slog.v(
@@ -2162,12 +2163,8 @@ public final class ActivityThread extends ClientTransactionHandler {
" finished=" +
(a.activity != null && a.activity.mFinished));
if (a.activity != null && !a.activity.mFinished) {
- try {
- am.activityIdle(a.token, a.createdConfig, stopProfiling);
- a.createdConfig = null;
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ ac.activityIdle(a.token, a.createdConfig, stopProfiling);
+ a.createdConfig = null;
}
prev = a;
a = a.nextIdle;
@@ -3576,13 +3573,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
- final int displayId;
- try {
- displayId = ActivityTaskManager.getService().getDisplayId(r.token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
+ final int displayId = ActivityClient.getInstance().getDisplayId(r.token);
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
@@ -3669,13 +3660,8 @@ public final class ActivityThread extends ClientTransactionHandler {
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
- try {
- ActivityTaskManager.getService()
- .finishActivity(r.token, Activity.RESULT_CANCELED, null,
- Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED,
+ null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
}
return a;
@@ -3705,12 +3691,8 @@ public final class ActivityThread extends ClientTransactionHandler {
smallest.put(config.smallestScreenWidthDp, 0);
}
}
- try {
- ActivityTaskManager.getService().reportSizeConfigurations(r.token,
- horizontal.copyKeys(), vertical.copyKeys(), smallest.copyKeys());
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().reportSizeConfigurations(r.token, horizontal.copyKeys(),
+ vertical.copyKeys(), smallest.copyKeys());
}
private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) {
@@ -4559,12 +4541,8 @@ public final class ActivityThread extends ClientTransactionHandler {
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
- try {
- willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(
- a.getActivityToken());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ willBeVisible = ActivityClient.getInstance().willActivityBeVisible(
+ a.getActivityToken());
}
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
@@ -5209,11 +5187,7 @@ public final class ActivityThread extends ClientTransactionHandler {
((ContextImpl) c).scheduleFinalCleanup(r.activity.getClass().getName(), "Activity");
}
if (finishing) {
- try {
- ActivityTaskManager.getService().activityDestroyed(r.token);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().activityDestroyed(r.token);
}
mSomeActivitiesChanged = true;
}
@@ -5477,13 +5451,9 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public void reportRelaunch(ActivityClientRecord r, PendingTransactionActions pendingActions) {
- try {
- ActivityTaskManager.getService().activityRelaunched(r.token);
- if (pendingActions.shouldReportRelaunchToWindowManager() && r.window != null) {
- r.window.reportActivityRelaunched();
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ ActivityClient.getInstance().activityRelaunched(r.token);
+ if (pendingActions.shouldReportRelaunchToWindowManager() && r.window != null) {
+ r.window.reportActivityRelaunched();
}
}
@@ -5623,10 +5593,13 @@ public final class ActivityThread extends ClientTransactionHandler {
// If the new config is the same as the config this Activity is already running with and
// the override config also didn't change, then don't bother calling
// onConfigurationChanged.
+ // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
+ // ResourcesImpl constructions.
final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
- if (diff == 0 && !movedToDifferentDisplay
- && mResourcesManager.isSameResourcesOverrideConfig(activityToken,
- amOverrideConfig)) {
+
+ if (diff == 0 && !shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
+ && !movedToDifferentDisplay && mResourcesManager.isSameResourcesOverrideConfig(
+ activityToken, amOverrideConfig)) {
// Nothing significant, don't proceed with updating and reporting.
return null;
} else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
@@ -5678,6 +5651,26 @@ public final class ActivityThread extends ClientTransactionHandler {
return configToReport;
}
+ // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
+ // constructions.
+ /**
+ * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
+ * should be updated.
+ *
+ * @see WindowManager#getCurrentWindowMetrics()
+ * @see WindowManager#getMaximumWindowMetrics()
+ */
+ private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
+ @NonNull Configuration newConfig) {
+ final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
+ final Rect newBounds = newConfig.windowConfiguration.getBounds();
+
+ final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
+ final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
+
+ return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
+ }
+
public final void applyConfigurationToResources(Configuration config) {
synchronized (mResourcesManager) {
mResourcesManager.applyConfigurationToResourcesLocked(config, null);
@@ -5912,7 +5905,7 @@ public final class ActivityThread extends ClientTransactionHandler {
/**
* Sets the supplied {@code overrideConfig} as pending for the {@code activityToken}. Calling
* this method prevents any calls to
- * {@link #handleActivityConfigurationChanged(IBinder, Configuration, int, boolean)} from
+ * {@link #handleActivityConfigurationChanged(ActivityClientRecord, Configuration, int)} from
* processing any configurations older than {@code overrideConfig}.
*/
@Override
@@ -5934,8 +5927,8 @@ public final class ActivityThread extends ClientTransactionHandler {
/**
* Handle new activity configuration and/or move to a different display. This method is a noop
- * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been called with
- * a newer config than {@code overrideConfig}.
+ * if {@link #updatePendingActivityConfiguration(ActivityClientRecord, Configuration)} has been
+ * called with a newer config than {@code overrideConfig}.
*
* @param r Target activity record.
* @param overrideConfig Activity override config.
@@ -6000,7 +5993,7 @@ public final class ActivityThread extends ClientTransactionHandler {
/**
* Checks if the display id of activity is different from the given one. Note that
- * {@link #INVALID_DISPLAY} means no difference.
+ * {@link Display#INVALID_DISPLAY} means no difference.
*/
private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) {
return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId();
@@ -6417,11 +6410,13 @@ public final class ActivityThread extends ClientTransactionHandler {
*/
LocaleList.setDefault(data.config.getLocales());
- try {
- Typeface.setSystemFontMap(data.mSerializedSystemFontMap);
- } catch (IOException | ErrnoException e) {
- Slog.e(TAG, "Failed to parse serialized system font map");
- Typeface.loadPreinstalledSystemFontMap();
+ if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+ try {
+ Typeface.setSystemFontMap(data.mSerializedSystemFontMap);
+ } catch (IOException | ErrnoException e) {
+ Slog.e(TAG, "Failed to parse serialized system font map");
+ Typeface.loadPreinstalledSystemFontMap();
+ }
}
synchronized (mResourcesManager) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 34437afb614a..3642d318e820 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.annotation.XmlRes;
+import android.app.role.RoleManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -2306,20 +2307,14 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public String getDefaultBrowserPackageNameAsUser(int userId) {
- try {
- return mPermissionManager.getDefaultBrowser(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ RoleManager roleManager = mContext.getSystemService(RoleManager.class);
+ return roleManager.getBrowserRoleHolder(userId);
}
@Override
public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
- try {
- return mPermissionManager.setDefaultBrowser(packageName, userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ RoleManager roleManager = mContext.getSystemService(RoleManager.class);
+ return roleManager.setBrowserRoleHolder(packageName, userId);
}
@Override
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
new file mode 100644
index 000000000000..f9449f241545
--- /dev/null
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.view.RemoteAnimationDefinition;
+
+/**
+ * Interface for the callback and request from an activity to system.
+ *
+ * {@hide}
+ */
+interface IActivityClientController {
+ oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling);
+ void activityResumed(in IBinder token);
+ void activityTopResumedStateLost();
+ void activityPaused(in IBinder token);
+ void activityStopped(in IBinder token, in Bundle state, in PersistableBundle persistentState,
+ in CharSequence description);
+ oneway void activityDestroyed(in IBinder token);
+ void activityRelaunched(in IBinder token);
+
+ void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
+ in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
+ boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
+ boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
+ boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
+ in Intent resultData);
+ boolean releaseActivityInstance(in IBinder token);
+ boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
+ boolean finishActivityAffinity(in IBinder token);
+ /** Finish all activities that were started for result from the specified activity. */
+ void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
+
+ boolean isTopOfTask(in IBinder token);
+ boolean willActivityBeVisible(in IBinder token);
+ int getDisplayId(in IBinder activityToken);
+ int getTaskForActivity(in IBinder token, in boolean onlyRoot);
+ ComponentName getCallingActivity(in IBinder token);
+ String getCallingPackage(in IBinder token);
+ Bundle getActivityOptions(in IBinder token);
+
+ void setRequestedOrientation(in IBinder token, int requestedOrientation);
+ int getRequestedOrientation(in IBinder token);
+
+ boolean convertFromTranslucent(in IBinder token);
+ boolean convertToTranslucent(in IBinder token, in Bundle options);
+
+ boolean isImmersive(in IBinder token);
+ void setImmersive(in IBinder token, boolean immersive);
+
+ boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
+ void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
+ void toggleFreeformWindowingMode(in IBinder token);
+
+ void startLockTaskModeByToken(in IBinder token);
+ void stopLockTaskModeByToken(in IBinder token);
+ oneway void showLockTaskEscapeMessage(in IBinder token);
+ void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
+
+ boolean showAssistFromActivity(in IBinder token, in Bundle args);
+ boolean isRootVoiceInteraction(in IBinder token);
+ void startLocalVoiceInteraction(in IBinder token, in Bundle options);
+ void stopLocalVoiceInteraction(in IBinder token);
+
+ void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
+ void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
+ void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+ void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
+ void overridePendingTransition(in IBinder token, in String packageName,
+ int enterAnim, int exitAnim);
+ int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
+
+ /** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
+ void setDisablePreviewScreenshots(in IBinder token, boolean disable);
+
+ /** Registers remote animations for a specific activity. */
+ void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
+
+ /** Unregisters all remote animations for a specific activity. */
+ void unregisterRemoteAnimations(in IBinder token);
+
+ /**
+ * Reports that an Activity received a back key press when there were no additional activities
+ * on the back stack.
+ */
+ void onBackPressedOnTaskRoot(in IBinder token);
+}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index ab48baea48e8..4b2557389382 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -22,6 +22,7 @@ import android.app.ApplicationErrorReport;
import android.app.ContentProviderHolder;
import android.app.GrantedUriPermission;
import android.app.IApplicationThread;
+import android.app.IActivityClientController;
import android.app.IActivityController;
import android.app.IAppTask;
import android.app.IAssistDataReceiver;
@@ -35,7 +36,6 @@ import android.app.IUidObserver;
import android.app.IUserSwitchObserver;
import android.app.Notification;
import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.assist.AssistContent;
@@ -63,7 +63,6 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.ParcelFileDescriptor;
-import android.os.PersistableBundle;
import android.os.StrictMode;
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
@@ -144,54 +143,25 @@ interface IActivityTaskManager {
int userId);
void unhandledBack();
- boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
- boolean finishActivityAffinity(in IBinder token);
-
- oneway void activityIdle(in IBinder token, in Configuration config,
- in boolean stopProfiling);
- void activityResumed(in IBinder token);
- void activityTopResumedStateLost();
- void activityPaused(in IBinder token);
- void activityStopped(in IBinder token, in Bundle state,
- in PersistableBundle persistentState, in CharSequence description);
- oneway void activityDestroyed(in IBinder token);
- void activityRelaunched(in IBinder token);
+
+ /** Returns an interface to control the activity related operations. */
+ IActivityClientController getActivityClientController();
+
int getFrontActivityScreenCompatMode();
void setFrontActivityScreenCompatMode(int mode);
- String getCallingPackage(in IBinder token);
- ComponentName getCallingActivity(in IBinder token);
void setFocusedTask(int taskId);
boolean removeTask(int taskId);
void removeAllVisibleRecentTasks();
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents);
- boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
- boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
- in Intent resultData);
void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
int flags, in Bundle options);
- int getTaskForActivity(in IBinder token, in boolean onlyRoot);
- /** Finish all activities that were started for result from the specified activity. */
- void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
int userId);
- boolean willActivityBeVisible(in IBinder token);
- void setRequestedOrientation(in IBinder token, int requestedOrientation);
- int getRequestedOrientation(in IBinder token);
- boolean convertFromTranslucent(in IBinder token);
- boolean convertToTranslucent(in IBinder token, in Bundle options);
- void notifyActivityDrawn(in IBinder token);
- void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
- int getDisplayId(in IBinder activityToken);
- boolean isImmersive(in IBinder token);
- void setImmersive(in IBinder token, boolean immersive);
boolean isTopActivityImmersive();
- boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
ActivityManager.TaskDescription getTaskDescription(int taskId);
- void overridePendingTransition(in IBinder token, in String packageName,
- int enterAnim, int exitAnim);
int getLaunchedFromUid(in IBinder activityToken);
String getLaunchedFromPackage(in IBinder activityToken);
- void reportAssistContextExtras(in IBinder token, in Bundle extras,
+ void reportAssistContextExtras(in IBinder assistToken, in Bundle extras,
in AssistStructure structure, in AssistContent content, in Uri referrer);
void setFocusedRootTask(int taskId);
@@ -199,24 +169,16 @@ interface IActivityTaskManager {
Rect getTaskBounds(int taskId);
void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
- void startLockTaskModeByToken(in IBinder token);
- void stopLockTaskModeByToken(in IBinder token);
void updateLockTaskPackages(int userId, in String[] packages);
boolean isInLockTaskMode();
int getLockTaskModeState();
- void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
- Bundle getActivityOptions(in IBinder token);
List<IBinder> getAppTasks(in String callingPackage);
void startSystemLockTaskMode(int taskId);
void stopSystemLockTaskMode();
void finishVoiceTask(in IVoiceInteractionSession session);
- boolean isTopOfTask(in IBinder token);
- void notifyLaunchTaskBehindComplete(in IBinder token);
- void notifyEnterAnimationComplete(in IBinder token);
int addAppTask(in IBinder activityToken, in Intent intent,
in ActivityManager.TaskDescription description, in Bitmap thumbnail);
Point getAppTaskThumbnailSize();
- boolean releaseActivityInstance(in IBinder token);
/**
* Only callable from the system. This token grants a temporary permission to call
* #startActivityAsCallerWithToken. The token will time out after
@@ -236,7 +198,6 @@ interface IActivityTaskManager {
void registerTaskStackListener(in ITaskStackListener listener);
void unregisterTaskStackListener(in ITaskStackListener listener);
void setTaskResizeable(int taskId, int resizeableMode);
- void toggleFreeformWindowingMode(in IBinder token);
/**
* Resize the task with given bounds
@@ -287,9 +248,6 @@ interface IActivityTaskManager {
boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras,
in IBinder activityToken, int flags);
boolean isAssistDataAllowedOnCurrentActivity();
- boolean showAssistFromActivity(in IBinder token, in Bundle args);
- boolean isRootVoiceInteraction(in IBinder token);
- oneway void showLockTaskEscapeMessage(in IBinder token);
/**
* Notify the system that the keyguard is going away.
@@ -302,15 +260,9 @@ interface IActivityTaskManager {
ComponentName getActivityClassForToken(in IBinder token);
String getPackageForToken(in IBinder token);
- void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
- in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
-
void suppressResizeConfigChanges(boolean suppress);
boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds);
- boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
- void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
void requestPictureInPictureMode(in IBinder token);
- IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
/**
* Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
@@ -343,9 +295,6 @@ interface IActivityTaskManager {
* are changing the docked stack size.
*/
void setSplitScreenResizing(boolean resizing);
- int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
- void startLocalVoiceInteraction(in IBinder token, in Bundle options);
- void stopLocalVoiceInteraction(in IBinder token);
boolean supportsLocalVoiceInteraction();
// Get device configuration
@@ -366,11 +315,6 @@ interface IActivityTaskManager {
ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution);
/**
- * See {@link android.app.Activity#setDisablePreviewScreenshots}
- */
- void setDisablePreviewScreenshots(IBinder token, boolean disable);
-
- /**
* It should only be called from home activity to remove its outdated snapshot. The home
* snapshot is used to speed up entering home from screen off. If the content of home activity
* is significantly different from before taking the snapshot, then the home activity can use
@@ -393,20 +337,6 @@ interface IActivityTaskManager {
boolean updateConfiguration(in Configuration values);
void updateLockTaskFeatures(int userId, int flags);
- void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
- void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
- void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
-
- /**
- * Registers remote animations for a specific activity.
- */
- void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
-
- /**
- * Unregisters all remote animations for a specific activity.
- */
- void unregisterRemoteAnimations(in IBinder token);
-
/**
* Registers a remote animation to be run for all activity starts from a certain package during
* a short predefined amount of time.
@@ -448,10 +378,4 @@ interface IActivityTaskManager {
* @param activityToken The token of the target activity to restart.
*/
void restartActivityProcessIfVisible(in IBinder activityToken);
-
- /**
- * Reports that an Activity received a back key press when there were no additional activities
- * on the back stack.
- */
- void onBackPressedOnTaskRoot(in IBinder activityToken);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 6535387acdf3..99785e1e73f4 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1284,7 +1284,7 @@ public final class LoadedApk {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
throw new RuntimeException(
"Unable to instantiate application " + appClass
- + ": " + e.toString(), e);
+ + " package " + mPackageName + ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a1135809fd4c..82f61a4c615a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -83,6 +83,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -6120,8 +6121,11 @@ public class Notification implements Parcelable
int defaultColor = mInNightMode ? Color.BLACK : Color.WHITE;
Resources.Theme theme = mContext.getTheme();
if (theme == null) {
+ // Running unit tests with mocked context
return defaultColor;
}
+ theme = new ContextThemeWrapper(mContext, R.style.Theme_DeviceDefault_DayNight)
+ .getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[]{R.attr.colorBackground});
if (ta == null) {
return defaultColor;
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 080aac9a9e6a..b1a8f9b0ba33 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -32,9 +32,12 @@ import android.os.Parcelable;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
import org.json.JSONException;
import org.json.JSONObject;
@@ -869,7 +872,7 @@ public final class NotificationChannel implements Parcelable {
* @hide
*/
public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
- populateFromXml(parser, true, context);
+ populateFromXml(XmlUtils.makeTyped(parser), true, context);
}
/**
@@ -877,13 +880,13 @@ public final class NotificationChannel implements Parcelable {
*/
@SystemApi
public void populateFromXml(XmlPullParser parser) {
- populateFromXml(parser, false, null);
+ populateFromXml(XmlUtils.makeTyped(parser), false, null);
}
/**
* If {@param forRestore} is true, {@param Context} MUST be non-null.
*/
- private void populateFromXml(XmlPullParser parser, boolean forRestore,
+ private void populateFromXml(TypedXmlPullParser parser, boolean forRestore,
@Nullable Context context) {
Preconditions.checkArgument(!forRestore || context != null,
"forRestore is true but got null context");
@@ -941,14 +944,14 @@ public final class NotificationChannel implements Parcelable {
*/
@SystemApi
public void writeXml(XmlSerializer out) throws IOException {
- writeXml(out, false, null);
+ writeXml(XmlUtils.makeTyped(out), false, null);
}
/**
* @hide
*/
public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
- writeXml(out, true, context);
+ writeXml(XmlUtils.makeTyped(out), true, context);
}
private Uri getSoundForBackup(Context context) {
@@ -967,7 +970,7 @@ public final class NotificationChannel implements Parcelable {
/**
* If {@param forBackup} is true, {@param Context} MUST be non-null.
*/
- private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context)
+ private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context)
throws IOException {
Preconditions.checkArgument(!forBackup || context != null,
"forBackup is true but got null context");
@@ -980,62 +983,58 @@ public final class NotificationChannel implements Parcelable {
out.attribute(null, ATT_DESC, getDescription());
}
if (getImportance() != DEFAULT_IMPORTANCE) {
- out.attribute(
- null, ATT_IMPORTANCE, Integer.toString(getImportance()));
+ out.attributeInt(null, ATT_IMPORTANCE, getImportance());
}
if (canBypassDnd()) {
- out.attribute(
- null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX));
+ out.attributeInt(null, ATT_PRIORITY, Notification.PRIORITY_MAX);
}
if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
- out.attribute(null, ATT_VISIBILITY,
- Integer.toString(getLockscreenVisibility()));
+ out.attributeInt(null, ATT_VISIBILITY, getLockscreenVisibility());
}
Uri sound = forBackup ? getSoundForBackup(context) : getSound();
if (sound != null) {
out.attribute(null, ATT_SOUND, sound.toString());
}
if (getAudioAttributes() != null) {
- out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
- out.attribute(null, ATT_CONTENT_TYPE,
- Integer.toString(getAudioAttributes().getContentType()));
- out.attribute(null, ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags()));
+ out.attributeInt(null, ATT_USAGE, getAudioAttributes().getUsage());
+ out.attributeInt(null, ATT_CONTENT_TYPE, getAudioAttributes().getContentType());
+ out.attributeInt(null, ATT_FLAGS, getAudioAttributes().getFlags());
}
if (shouldShowLights()) {
- out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights()));
+ out.attributeBoolean(null, ATT_LIGHTS, shouldShowLights());
}
if (getLightColor() != DEFAULT_LIGHT_COLOR) {
- out.attribute(null, ATT_LIGHT_COLOR, Integer.toString(getLightColor()));
+ out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor());
}
if (shouldVibrate()) {
- out.attribute(null, ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
+ out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate());
}
if (getVibrationPattern() != null) {
out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
}
if (getUserLockedFields() != 0) {
- out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
+ out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
}
if (isFgServiceShown()) {
- out.attribute(null, ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown()));
+ out.attributeBoolean(null, ATT_FG_SERVICE_SHOWN, isFgServiceShown());
}
if (canShowBadge()) {
- out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
+ out.attributeBoolean(null, ATT_SHOW_BADGE, canShowBadge());
}
if (isDeleted()) {
- out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted()));
+ out.attributeBoolean(null, ATT_DELETED, isDeleted());
}
if (getGroup() != null) {
out.attribute(null, ATT_GROUP, getGroup());
}
if (isBlockable()) {
- out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockable()));
+ out.attributeBoolean(null, ATT_BLOCKABLE_SYSTEM, isBlockable());
}
if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) {
- out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(getAllowBubbles()));
+ out.attributeInt(null, ATT_ALLOW_BUBBLE, getAllowBubbles());
}
if (getOriginalImportance() != DEFAULT_IMPORTANCE) {
- out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance()));
+ out.attributeInt(null, ATT_ORIG_IMP, getOriginalImportance());
}
if (getParentChannelId() != null) {
out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId());
@@ -1044,10 +1043,10 @@ public final class NotificationChannel implements Parcelable {
out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
}
if (isDemoted()) {
- out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
+ out.attributeBoolean(null, ATT_DEMOTE, isDemoted());
}
if (isImportantConversation()) {
- out.attribute(null, ATT_IMP_CONVERSATION, Boolean.toString(isImportantConversation()));
+ out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation());
}
// mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
@@ -1099,7 +1098,7 @@ public final class NotificationChannel implements Parcelable {
return record;
}
- private static AudioAttributes safeAudioAttributes(XmlPullParser parser) {
+ private static AudioAttributes safeAudioAttributes(TypedXmlPullParser parser) {
int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION);
int contentType = safeInt(parser, ATT_CONTENT_TYPE,
AudioAttributes.CONTENT_TYPE_SONIFICATION);
@@ -1111,32 +1110,20 @@ public final class NotificationChannel implements Parcelable {
.build();
}
- private static Uri safeUri(XmlPullParser parser, String att) {
+ private static Uri safeUri(TypedXmlPullParser parser, String att) {
final String val = parser.getAttributeValue(null, att);
return val == null ? null : Uri.parse(val);
}
- private static int safeInt(XmlPullParser parser, String att, int defValue) {
- final String val = parser.getAttributeValue(null, att);
- return tryParseInt(val, defValue);
- }
-
- private static int tryParseInt(String value, int defValue) {
- if (TextUtils.isEmpty(value)) return defValue;
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return defValue;
- }
+ private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
+ return parser.getAttributeInt(null, att, defValue);
}
- private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
- final String value = parser.getAttributeValue(null, att);
- if (TextUtils.isEmpty(value)) return defValue;
- return Boolean.parseBoolean(value);
+ private static boolean safeBool(TypedXmlPullParser parser, String att, boolean defValue) {
+ return parser.getAttributeBoolean(null, att, defValue);
}
- private static long[] safeLongArray(XmlPullParser parser, String att, long[] defValue) {
+ private static long[] safeLongArray(TypedXmlPullParser parser, String att, long[] defValue) {
final String attributeValue = parser.getAttributeValue(null, att);
if (TextUtils.isEmpty(attributeValue)) return defValue;
String[] values = attributeValue.split(DELIMITER);
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index ec7fa332b23f..cd6df0b231d9 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -23,12 +23,12 @@ import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import org.json.JSONException;
import org.json.JSONObject;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -228,22 +228,16 @@ public final class NotificationChannelGroup implements Parcelable {
/**
* @hide
*/
- public void populateFromXml(XmlPullParser parser) {
+ public void populateFromXml(TypedXmlPullParser parser) {
// Name, id, and importance are set in the constructor.
setDescription(parser.getAttributeValue(null, ATT_DESC));
- setBlocked(safeBool(parser, ATT_BLOCKED, false));
- }
-
- private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
- final String value = parser.getAttributeValue(null, att);
- if (TextUtils.isEmpty(value)) return defValue;
- return Boolean.parseBoolean(value);
+ setBlocked(parser.getAttributeBoolean(null, ATT_BLOCKED, false));
}
/**
* @hide
*/
- public void writeXml(XmlSerializer out) throws IOException {
+ public void writeXml(TypedXmlSerializer out) throws IOException {
out.startTag(null, TAG_GROUP);
out.attribute(null, ATT_ID, getId());
@@ -253,8 +247,8 @@ public final class NotificationChannelGroup implements Parcelable {
if (getDescription() != null) {
out.attribute(null, ATT_DESC, getDescription().toString());
}
- out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
- out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields));
+ out.attributeBoolean(null, ATT_BLOCKED, isBlocked());
+ out.attributeInt(null, ATT_USER_LOCKED, mUserLockedFields);
out.endTag(null, TAG_GROUP);
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 36d5b5eb9fdf..772833cc6d2d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1178,6 +1178,8 @@ public class ResourcesManager {
continue;
}
+ // TODO(b/173090263): Improve the performance of AssetManager & ResourcesImpl
+ // constructions.
final ResourcesImpl resourcesImpl =
findOrCreateResourcesImplForKeyLocked(newKey);
if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 7287acdd0241..392d6fbe53d6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -194,6 +194,7 @@ import android.telephony.TelephonyRegistryManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.uwb.UwbManager;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.WindowManager;
@@ -732,6 +733,14 @@ public final class SystemServiceRegistry {
return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
}});
+ registerService(Context.UWB_SERVICE, UwbManager.class,
+ new CachedServiceFetcher<UwbManager>() {
+ @Override
+ public UwbManager createService(ContextImpl ctx) {
+ return UwbManager.getInstance();
+ }
+ });
+
registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
new CachedServiceFetcher<Vibrator>() {
@Override
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 36241a8ebe08..61e93f73b094 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -70,6 +70,18 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
+ "include-filter": "android.autofillservice.cts.saveui.AutofillSaveDialogTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ],
+ "file_patterns": ["(/|^)Activity.java"]
+ },
+ {
+ "name": "CtsAutoFillServiceTestCases",
+ "options": [
+ {
"include-filter": "android.autofillservice.cts.saveui.PreSimpleSaveActivityTest"
},
{
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 1ee8e4fce58b..41256d09d0ed 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -37,11 +37,12 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -472,16 +473,15 @@ public final class DeviceAdminInfo implements Parcelable {
}
/** @hide */
- public void writePoliciesToXml(XmlSerializer out)
+ public void writePoliciesToXml(TypedXmlSerializer out)
throws IllegalArgumentException, IllegalStateException, IOException {
- out.attribute(null, "flags", Integer.toString(mUsesPolicies));
+ out.attributeInt(null, "flags", mUsesPolicies);
}
/** @hide */
- public void readPoliciesFromXml(XmlPullParser parser)
+ public void readPoliciesFromXml(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
- mUsesPolicies = Integer.parseInt(
- parser.getAttributeValue(null, "flags"));
+ mUsesPolicies = parser.getAttributeInt(null, "flags");
}
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2cda017bbd1a..4095acc239e9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1207,7 +1207,7 @@ public class DevicePolicyManager {
* <ul>
* <li>By the admin app when performing the admin-integrated
* provisioning flow as a result of the {@link #ACTION_GET_PROVISIONING_MODE} activity</li>
- * <li>With intent action {@link #ACTION_PROVISION_MANAGED_DEVICE}</li>
+ * <li>For managed account enrollment</li>
* </ul>
*
* <p>If the education screens are skipped, it is the admin application's responsibility
@@ -1230,9 +1230,37 @@ public class DevicePolicyManager {
"android.app.extra.PROVISIONING_USE_MOBILE_DATA";
/**
+ * Possible values for {@link #EXTRA_PROVISIONING_TRIGGER}.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "PROVISIONING_TRIGGER_" }, value = {
+ PROVISIONING_TRIGGER_UNSPECIFIED,
+ PROVISIONING_TRIGGER_CLOUD_ENROLLMENT,
+ PROVISIONING_TRIGGER_QR_CODE,
+ PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER,
+ PROVISIONING_TRIGGER_MANAGED_ACCOUNT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProvisioningTrigger {}
+
+ /**
+ * Possible values for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES}.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "SUPPORTED_MODES_" }, value = {
+ SUPPORTED_MODES_ORGANIZATION_OWNED,
+ SUPPORTED_MODES_PERSONALLY_OWNED,
+ SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProvisioningConfiguration {}
+
+ /**
* A String extra holding the provisioning trigger. It could be one of
* {@link #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT}, {@link #PROVISIONING_TRIGGER_QR_CODE},
- * {@link #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER} or {@link
+ * {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} or {@link
* #PROVISIONING_TRIGGER_UNSPECIFIED}.
*
* <p>Use in an intent with action {@link
@@ -1248,7 +1276,7 @@ public class DevicePolicyManager {
* trigger has not been specified.
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
- * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+ * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @hide
*/
@SystemApi
@@ -1258,7 +1286,7 @@ public class DevicePolicyManager {
* A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
* trigger is cloud enrollment.
* @see #PROVISIONING_TRIGGER_QR_CODE
- * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+ * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
* @hide
*/
@@ -1269,7 +1297,7 @@ public class DevicePolicyManager {
* A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
* trigger is the QR code scanner.
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
- * @see #PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER
+ * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
* @hide
*/
@@ -1279,15 +1307,78 @@ public class DevicePolicyManager {
/**
* A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
* trigger is persistent device owner enrollment.
+ * @deprecated Use the broader {@link #PROVISIONING_TRIGGER_MANAGED_ACCOUNT} instead
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
* @hide
*/
@SystemApi
+ @Deprecated
public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3;
/**
+ * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning
+ * trigger is managed account enrollment.
+ * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+ * @see #PROVISIONING_TRIGGER_QR_CODE
+ * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @hide
+ */
+ @SystemApi
+ public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4;
+
+ /**
+ * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is
+ * organization-owned.
+ *
+ * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE}
+ * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
+ * contain {@link #PROVISIONING_MODE_MANAGED_PROFILE} and {@link
+ * #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
+ *
+ * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
+ * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
+ * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1;
+
+ /**
+ * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is
+ * personally-owned.
+ *
+ * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE}
+ * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
+ * contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2;
+
+ /**
+ * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning could
+ * be organization-owned or personally-owned.
+ *
+ * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE}
+ * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
+ * contain {@link
+ * #PROVISIONING_MODE_MANAGED_PROFILE}, {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE} and
+ * {@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE}.
+ *
+ * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
+ * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
+ * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3;
+
+ /**
* This MIME type is used for starting the device owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -2380,16 +2471,57 @@ public class DevicePolicyManager {
/**
* An intent extra holding the provisioning mode returned by the administrator.
- * The value for this extra should be one of the following:
- * <ul>
- * <li>{@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}</li>
- * <li>{@link #PROVISIONING_MODE_MANAGED_PROFILE}</li>
- * </ul>
+ * The value of this extra must be one of the values provided in {@link
+ * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES}, which is provided as an intent extra to
+ * the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity.
+ *
+ * @see #PROVISIONING_MODE_FULLY_MANAGED_DEVICE
+ * @see #PROVISIONING_MODE_MANAGED_PROFILE
*/
public static final String EXTRA_PROVISIONING_MODE =
"android.app.extra.PROVISIONING_MODE";
/**
+ * An integer extra indication what provisioning modes should be available for the admin app
+ * to pick.
+ *
+ * <p>The default value is {@link #SUPPORTED_MODES_ORGANIZATION_OWNED}.
+ *
+ * <p>The value of this extra will determine the contents of the {@link
+ * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array that is passed to the admin app as an
+ * extra to its {@link #ACTION_GET_PROVISIONING_MODE} activity.
+ *
+ * <p>If one of the possible admin app choices is a personally-owned work profile, then the
+ * IMEI and serial number will not be passed to the admin app's {@link
+ * #ACTION_GET_PROVISIONING_MODE} activity via the {@link #EXTRA_PROVISIONING_IMEI} and {@link
+ * #EXTRA_PROVISIONING_SERIAL_NUMBER} respectively.
+ *
+ * <p>This extra is only respected when provided alongside the {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action.
+ *
+ * @see #SUPPORTED_MODES_ORGANIZATION_OWNED
+ * @see #SUPPORTED_MODES_PERSONALLY_OWNED
+ * @see #SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PROVISIONING_SUPPORTED_MODES =
+ "android.app.extra.PROVISIONING_SUPPORTED_MODES";
+
+ /**
+ * An {@link ArrayList} of {@link Integer} extra specifying the allowed provisioning modes.
+ * <p>This extra will be passed to the admin app's {@link #ACTION_GET_PROVISIONING_MODE}
+ * activity, whose result intent must contain {@link #EXTRA_PROVISIONING_MODE} set to one of
+ * the values in this array.
+ * <p>If the value set to {@link #EXTRA_PROVISIONING_MODE} is not in the array,
+ * provisioning will fail.
+ * @see #PROVISIONING_MODE_MANAGED_PROFILE
+ * @see #PROVISIONING_MODE_FULLY_MANAGED_DEVICE
+ */
+ public static final String EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES =
+ "android.app.extra.PROVISIONING_ALLOWED_PROVISIONING_MODES";
+
+ /**
* The provisioning mode for fully managed device.
*/
public static final int PROVISIONING_MODE_FULLY_MANAGED_DEVICE = 1;
@@ -2400,6 +2532,11 @@ public class DevicePolicyManager {
public static final int PROVISIONING_MODE_MANAGED_PROFILE = 2;
/**
+ * The provisioning mode for a work profile on a personal device.
+ */
+ public static final int PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE = 3;
+
+ /**
* Activity action: Starts the administrator to show policy compliance for the provisioning.
* This action is used any time that the administrator has an opportunity to show policy
* compliance before the end of setup wizard. This could happen as part of the admin-integrated
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
index aa94e817c152..40ae1f0c11ea 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -26,10 +26,10 @@ import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -201,10 +201,10 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
* @hide
*/
@Nullable
- public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) {
+ public static FactoryResetProtectionPolicy readFromXml(@NonNull TypedXmlPullParser parser) {
try {
- boolean factoryResetProtectionEnabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_ENABLED));
+ boolean factoryResetProtectionEnabled = parser.getAttributeBoolean(null,
+ KEY_FACTORY_RESET_PROTECTION_ENABLED, false);
List<String> factoryResetProtectionAccounts = new ArrayList<>();
int outerDepth = parser.getDepth();
@@ -232,9 +232,9 @@ public final class FactoryResetProtectionPolicy implements Parcelable {
/**
* @hide
*/
- public void writeToXml(@NonNull XmlSerializer out) throws IOException {
- out.attribute(null, KEY_FACTORY_RESET_PROTECTION_ENABLED,
- Boolean.toString(mFactoryResetProtectionEnabled));
+ public void writeToXml(@NonNull TypedXmlSerializer out) throws IOException {
+ out.attributeBoolean(null, KEY_FACTORY_RESET_PROTECTION_ENABLED,
+ mFactoryResetProtectionEnabled);
for (String account : mFactoryResetProtectionAccounts) {
out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
out.attribute(null, ATTR_VALUE, account);
diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java
index 53b238633fec..b88bf76c96ca 100644
--- a/core/java/android/app/admin/SystemUpdateInfo.java
+++ b/core/java/android/app/admin/SystemUpdateInfo.java
@@ -21,9 +21,11 @@ import android.annotation.Nullable;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
+import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -34,6 +36,7 @@ import java.util.Objects;
* A class containing information about a pending system update.
*/
public final class SystemUpdateInfo implements Parcelable {
+ private static final String TAG = "SystemUpdateInfo";
/**
* Represents it is unknown whether the system update is a security patch.
@@ -125,27 +128,32 @@ public final class SystemUpdateInfo implements Parcelable {
};
/** @hide */
- public void writeToXml(XmlSerializer out, String tag) throws IOException {
+ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException {
out.startTag(null, tag);
- out.attribute(null, ATTR_RECEIVED_TIME, String.valueOf(mReceivedTime));
- out.attribute(null, ATTR_SECURITY_PATCH_STATE, String.valueOf(mSecurityPatchState));
+ out.attributeLong(null, ATTR_RECEIVED_TIME, mReceivedTime);
+ out.attributeInt(null, ATTR_SECURITY_PATCH_STATE, mSecurityPatchState);
out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT);
out.endTag(null, tag);
}
/** @hide */
@Nullable
- public static SystemUpdateInfo readFromXml(XmlPullParser parser) {
+ public static SystemUpdateInfo readFromXml(TypedXmlPullParser parser) {
// If an OTA has been applied (build fingerprint has changed), discard stale info.
final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD );
if (!Build.FINGERPRINT.equals(buildFingerprint)) {
return null;
}
- final long receivedTime =
- Long.parseLong(parser.getAttributeValue(null, ATTR_RECEIVED_TIME));
- final int securityPatchState =
- Integer.parseInt(parser.getAttributeValue(null, ATTR_SECURITY_PATCH_STATE));
- return new SystemUpdateInfo(receivedTime, securityPatchState);
+ try {
+ final long receivedTime =
+ parser.getAttributeLong(null, ATTR_RECEIVED_TIME);
+ final int securityPatchState =
+ parser.getAttributeInt(null, ATTR_SECURITY_PATCH_STATE);
+ return new SystemUpdateInfo(receivedTime, securityPatchState);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Load xml failed", e);
+ return null;
+ }
}
@Override
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 2ba2c0425b2f..68ac4ccf210a 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -26,10 +26,10 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.Pair;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -741,38 +741,31 @@ public final class SystemUpdatePolicy implements Parcelable {
* system server from a validated policy object previously.
* @hide
*/
- public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) {
+ public static SystemUpdatePolicy restoreFromXml(TypedXmlPullParser parser) {
try {
SystemUpdatePolicy policy = new SystemUpdatePolicy();
- String value = parser.getAttributeValue(null, KEY_POLICY_TYPE);
- if (value != null) {
- policy.mPolicyType = Integer.parseInt(value);
-
- value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START);
- if (value != null) {
- policy.mMaintenanceWindowStart = Integer.parseInt(value);
- }
- value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END);
- if (value != null) {
- policy.mMaintenanceWindowEnd = Integer.parseInt(value);
+ policy.mPolicyType =
+ parser.getAttributeInt(null, KEY_POLICY_TYPE, TYPE_UNKNOWN);
+ policy.mMaintenanceWindowStart =
+ parser.getAttributeInt(null, KEY_INSTALL_WINDOW_START, 0);
+ policy.mMaintenanceWindowEnd =
+ parser.getAttributeInt(null, KEY_INSTALL_WINDOW_END, 0);
+
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != END_DOCUMENT
+ && (type != END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == END_TAG || type == TEXT) {
+ continue;
}
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != END_DOCUMENT
- && (type != END_TAG || parser.getDepth() > outerDepth)) {
- if (type == END_TAG || type == TEXT) {
- continue;
- }
- if (!parser.getName().equals(KEY_FREEZE_TAG)) {
- continue;
- }
- policy.mFreezePeriods.add(new FreezePeriod(
- MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)),
- MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END))));
+ if (!parser.getName().equals(KEY_FREEZE_TAG)) {
+ continue;
}
- return policy;
+ policy.mFreezePeriods.add(new FreezePeriod(
+ MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)),
+ MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END))));
}
+ return policy;
} catch (NumberFormatException | XmlPullParserException | IOException e) {
// Fail through
Log.w(TAG, "Load xml failed", e);
@@ -783,10 +776,10 @@ public final class SystemUpdatePolicy implements Parcelable {
/**
* @hide
*/
- public void saveToXml(XmlSerializer out) throws IOException {
- out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType));
- out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
- out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
+ public void saveToXml(TypedXmlSerializer out) throws IOException {
+ out.attributeInt(null, KEY_POLICY_TYPE, mPolicyType);
+ out.attributeInt(null, KEY_INSTALL_WINDOW_START, mMaintenanceWindowStart);
+ out.attributeInt(null, KEY_INSTALL_WINDOW_END, mMaintenanceWindowEnd);
for (int i = 0; i < mFreezePeriods.size(); i++) {
FreezePeriod interval = mFreezePeriods.get(i);
out.startTag(null, KEY_FREEZE_TAG);
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 44a4b78e1afa..673de8fa7c8c 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -205,13 +205,16 @@ public class BackupManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
OperationType.BACKUP,
- OperationType.MIGRATION
+ OperationType.MIGRATION,
+ OperationType.ADB_BACKUP,
})
public @interface OperationType {
- // A regular backup / restore operation.
+ // A backup / restore to / from an off-device location, e.g. cloud.
int BACKUP = 0;
- // A full migration: all app data for non-system apps is eligible.
+ // A direct transfer to another device.
int MIGRATION = 1;
+ // Backup via adb, data saved on the host machine.
+ int ADB_BACKUP = 3;
}
private Context mContext;
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 6d790b381ace..5fc25f0422e2 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -53,5 +53,9 @@ interface IRoleManager {
List<String> getHeldRolesFromController(in String packageName);
- String getDefaultSmsPackage(int userId);
+ String getBrowserRoleHolder(int userId);
+
+ boolean setBrowserRoleHolder(String packageName, int userId);
+
+ String getSmsRoleHolder(int userId);
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 408ce0f2ab1a..8b2e07b09701 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -613,12 +613,56 @@ public final class RoleManager {
}
/**
- * Allows getting the role holder for {@link #ROLE_SMS} without
- * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by
- * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}
+ * Get the role holder of {@link #ROLE_BROWSER} without requiring
+ * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in
+ * {@link android.content.pm.PackageManager#getDefaultBrowserPackageNameAsUser(int)}
+ *
+ * @param userId the user ID
+ * @return the package name of the default browser, or {@code null} if none
+ *
+ * @hide
+ */
+ @Nullable
+ //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public String getBrowserRoleHolder(@UserIdInt int userId) {
+ try {
+ return mService.getBrowserRoleHolder(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the role holder of {@link #ROLE_BROWSER} requiring
+ * {@link Manifest.permission.SET_PREFERRED_APPLICATIONS} instead of
+ * {@link Manifest.permission#MANAGE_ROLE_HOLDERS}, as in
+ * {@link android.content.pm.PackageManager#setDefaultBrowserPackageNameAsUser(String, int)}
+ *
+ * @param packageName the package name of the default browser, or {@code null} if none
+ * @param userId the user ID
+ * @return whether the default browser was set successfully
+ *
+ * @hide
+ */
+ @Nullable
+ @RequiresPermission(Manifest.permission.SET_PREFERRED_APPLICATIONS)
+ //@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) {
+ try {
+ return mService.setBrowserRoleHolder(packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Allows getting the role holder for {@link #ROLE_SMS} without requiring
+ * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in
+ * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}.
+ *
+ * @param userId the user ID to get the default SMS package for
+ * @return the package name of the default SMS app, or {@code null} if none
*
- * @param userId The user ID to get the default SMS package for.
- * @return the package name of the default SMS app, or {@code null} if not configured.
* @hide
*/
@Nullable
@@ -626,7 +670,7 @@ public final class RoleManager {
@TestApi
public String getSmsRoleHolder(@UserIdInt int userId) {
try {
- return mService.getDefaultSmsPackage(userId);
+ return mService.getSmsRoleHolder(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index f7c645e7cb38..813e0f93a1f7 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -20,12 +20,11 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
+import android.app.ActivityClient;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.RemoteException;
import android.os.Trace;
/**
@@ -61,12 +60,8 @@ public class PauseActivityItem extends ActivityLifecycleItem {
if (mDontReport) {
return;
}
- try {
- // TODO(lifecycler): Use interface callback instead of AMS.
- ActivityTaskManager.getService().activityPaused(token);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ // TODO(lifecycler): Use interface callback instead of actual implementation.
+ ActivityClient.getInstance().activityPaused(token);
}
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index 52ba8fb73f5f..a47fe821cd01 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -18,13 +18,11 @@ package android.app.servertransaction;
import static android.app.ActivityThread.DEBUG_MEMORY_TRIM;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
+import android.app.ActivityClient;
import android.app.ActivityThread.ActivityClientRecord;
import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.RemoteException;
import android.os.TransactionTooLargeException;
import android.util.Log;
import android.util.LogWriter;
@@ -142,9 +140,9 @@ public class PendingTransactionActions {
try {
if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
// TODO(lifecycler): Use interface callback instead of AMS.
- ActivityTaskManager.getService().activityStopped(
+ ActivityClient.getInstance().activityStopped(
mActivity.token, mState, mPersistentState, mDescription);
- } catch (RemoteException ex) {
+ } catch (RuntimeException ex) {
// Dump statistics about bundle to help developers debug
final LogWriter writer = new LogWriter(Log.WARN, TAG);
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -153,12 +151,12 @@ public class PendingTransactionActions {
pw.println("PersistableBundle stats:");
Bundle.dumpStats(pw, mPersistentState);
- if (ex instanceof TransactionTooLargeException
+ if (ex.getCause() instanceof TransactionTooLargeException
&& mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
return;
}
- throw ex.rethrowFromSystemServer();
+ throw ex;
}
}
}
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index b4523f581ba8..d451599cc7b0 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -20,13 +20,12 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityClient;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.RemoteException;
import android.os.Trace;
/**
@@ -60,12 +59,8 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
- try {
- // TODO(lifecycler): Use interface callback instead of AMS.
- ActivityTaskManager.getService().activityResumed(token);
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ // TODO(lifecycler): Use interface callback instead of actual implementation.
+ ActivityClient.getInstance().activityResumed(token);
}
@Override
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
index 2b0c1b9869e0..5cd3d68f0326 100644
--- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -19,12 +19,11 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
+import android.app.ActivityClient;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
-import android.os.RemoteException;
import android.os.Trace;
/**
@@ -56,11 +55,7 @@ public class TopResumedActivityChangeItem extends ActivityTransactionItem {
// 2. Activity wasn't RESUMED yet, which means that it didn't receive the top state yet.
// 3. Activity is PAUSED or in other lifecycle state after PAUSED. In this case top resumed
// state loss was already called right before pausing.
- try {
- ActivityTaskManager.getService().activityTopResumedStateLost();
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
+ ActivityClient.getInstance().activityTopResumedStateLost();
}
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 3522b1b8aff5..081f4fdc1b12 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -383,6 +383,10 @@ public final class UsageEvents implements Parcelable {
public int mClassToken = UNASSIGNED_TOKEN;
/**
+ * Uniquely identifies an activity. It's possible for two activities with the same
+ * pkg/class name to be in lifecycle at the same time. The mInstanceId is guaranteed to be
+ * unique per activity across all apps (not just within a single app).
+ *
* {@hide}
*/
public int mInstanceId;
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 130a20dfb07b..c4333ac44e34 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -68,6 +68,9 @@ public class AppWidgetProviderInfo implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface ResizeModeFlags {}
+ /** {@hide} */
+ public static final int WIDGET_CATEGORY_UNKNOWN = -1;
+
/**
* Indicates that the widget can be displayed on the home screen. This is the default value.
*/
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 603a7ff29ea3..7fe18a0704ba 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -426,13 +426,13 @@ public final class BluetoothClass implements Parcelable {
return false;
}
} else if (profile == PROFILE_HID) {
- return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL;
+ return getMajorDeviceClass() == Device.Major.PERIPHERAL;
} else if (profile == PROFILE_PANU || profile == PROFILE_NAP) {
// No good way to distinguish between the two, based on class bits.
if (hasService(Service.NETWORKING)) {
return true;
}
- return (getDeviceClass() & Device.Major.NETWORKING) == Device.Major.NETWORKING;
+ return getMajorDeviceClass() == Device.Major.NETWORKING;
} else {
return false;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8f92bf1e3253..d920fb3e97e6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3544,6 +3544,7 @@ public abstract class Context {
LIGHTS_SERVICE,
//@hide: PEOPLE_SERVICE,
//@hide: DEVICE_STATE_SERVICE,
+ UWB_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -5260,6 +5261,15 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.uwb.UwbManager}.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String UWB_SERVICE = "uwb";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.DreamManager} for controlling Dream states.
*
* @see #getSystemService(String)
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index b0c47bdfd864..56da3cb0eb02 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -270,7 +270,7 @@ public class ContextWrapper extends Context {
}
@Override
- public File getExternalFilesDir(String type) {
+ public @Nullable File getExternalFilesDir(@Nullable String type) {
return mBase.getExternalFilesDir(type);
}
@@ -300,7 +300,7 @@ public class ContextWrapper extends Context {
}
@Override
- public File getExternalCacheDir() {
+ public @Nullable File getExternalCacheDir() {
return mBase.getExternalCacheDir();
}
@@ -322,7 +322,7 @@ public class ContextWrapper extends Context {
/** @hide **/
@Override
- public File getPreloadsFileCache() {
+ public @Nullable File getPreloadsFileCache() {
return mBase.getPreloadsFileCache();
}
@@ -333,7 +333,7 @@ public class ContextWrapper extends Context {
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
- DatabaseErrorHandler errorHandler) {
+ @Nullable DatabaseErrorHandler errorHandler) {
return mBase.openOrCreateDatabase(name, mode, factory, errorHandler);
}
@@ -412,7 +412,7 @@ public class ContextWrapper extends Context {
/** @hide **/
public void startActivityForResult(
- String who, Intent intent, int requestCode, Bundle options) {
+ String who, Intent intent, int requestCode, @Nullable Bundle options) {
mBase.startActivityForResult(who, intent, requestCode, options);
}
@@ -422,13 +422,13 @@ public class ContextWrapper extends Context {
}
@Override
- public void startActivity(Intent intent, Bundle options) {
+ public void startActivity(Intent intent, @Nullable Bundle options) {
mBase.startActivity(intent, options);
}
/** @hide */
@Override
- public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ public void startActivityAsUser(Intent intent, @Nullable Bundle options, UserHandle user) {
mBase.startActivityAsUser(intent, options, user);
}
@@ -438,13 +438,14 @@ public class ContextWrapper extends Context {
}
@Override
- public void startActivities(Intent[] intents, Bundle options) {
+ public void startActivities(Intent[] intents, @Nullable Bundle options) {
mBase.startActivities(intents, options);
}
/** @hide */
@Override
- public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
+ public int startActivitiesAsUser(Intent[] intents, @Nullable Bundle options,
+ UserHandle userHandle) {
return mBase.startActivitiesAsUser(intents, options, userHandle);
}
@@ -472,7 +473,7 @@ public class ContextWrapper extends Context {
}
@Override
- public void sendBroadcast(Intent intent, String receiverPermission) {
+ public void sendBroadcast(Intent intent, @Nullable String receiverPermission) {
mBase.sendBroadcast(intent, receiverPermission);
}
@@ -493,27 +494,28 @@ public class ContextWrapper extends Context {
/** @hide */
@SystemApi
@Override
- public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
+ public void sendBroadcast(Intent intent, @Nullable String receiverPermission,
+ @Nullable Bundle options) {
mBase.sendBroadcast(intent, receiverPermission, options);
}
/** @hide */
@Override
- public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+ public void sendBroadcast(Intent intent, @Nullable String receiverPermission, int appOp) {
mBase.sendBroadcast(intent, receiverPermission, appOp);
}
@Override
public void sendOrderedBroadcast(Intent intent,
- String receiverPermission) {
+ @Nullable String receiverPermission) {
mBase.sendOrderedBroadcast(intent, receiverPermission);
}
@Override
public void sendOrderedBroadcast(
- Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData,
- Bundle initialExtras) {
+ Intent intent, @Nullable String receiverPermission,
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
mBase.sendOrderedBroadcast(intent, receiverPermission,
resultReceiver, scheduler, initialCode,
initialData, initialExtras);
@@ -523,10 +525,9 @@ public class ContextWrapper extends Context {
@SystemApi
@Override
public void sendOrderedBroadcast(
- Intent intent, String receiverPermission, Bundle options,
- BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData,
- Bundle initialExtras) {
+ Intent intent, @Nullable String receiverPermission, @Nullable Bundle options,
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
mBase.sendOrderedBroadcast(intent, receiverPermission,
options, resultReceiver, scheduler, initialCode,
initialData, initialExtras);
@@ -535,9 +536,9 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
public void sendOrderedBroadcast(
- Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData,
- Bundle initialExtras) {
+ Intent intent, @Nullable String receiverPermission, int appOp,
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
mBase.sendOrderedBroadcast(intent, receiverPermission, appOp,
resultReceiver, scheduler, initialCode,
initialData, initialExtras);
@@ -557,21 +558,22 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
public void sendBroadcastAsUser(Intent intent, UserHandle user,
- String receiverPermission, Bundle options) {
+ @Nullable String receiverPermission, @Nullable Bundle options) {
mBase.sendBroadcastAsUser(intent, user, receiverPermission, options);
}
/** @hide */
@Override
public void sendBroadcastAsUser(Intent intent, UserHandle user,
- String receiverPermission, int appOp) {
+ @Nullable String receiverPermission, int appOp) {
mBase.sendBroadcastAsUser(intent, user, receiverPermission, appOp);
}
@Override
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
- int initialCode, String initialData, Bundle initialExtras) {
+ @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver,
+ @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+ @Nullable Bundle initialExtras) {
mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, resultReceiver,
scheduler, initialCode, initialData, initialExtras);
}
@@ -579,8 +581,9 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ @Nullable String receiverPermission, int appOp,
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, resultReceiver,
scheduler, initialCode, initialData, initialExtras);
}
@@ -588,8 +591,9 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
- String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ @Nullable String receiverPermission, int appOp, @Nullable Bundle options,
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
mBase.sendOrderedBroadcastAsUser(intent, user, receiverPermission, appOp, options,
resultReceiver, scheduler, initialCode, initialData, initialExtras);
}
@@ -621,10 +625,9 @@ public class ContextWrapper extends Context {
@Override
@Deprecated
- public void sendStickyOrderedBroadcast(
- Intent intent, BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData,
- Bundle initialExtras) {
+ public void sendStickyOrderedBroadcast(Intent intent,
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras) {
mBase.sendStickyOrderedBroadcast(intent,
resultReceiver, scheduler, initialCode,
initialData, initialExtras);
@@ -645,16 +648,17 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
@Deprecated
- public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user,
+ @Nullable Bundle options) {
mBase.sendStickyBroadcastAsUser(intent, user, options);
}
@Override
@Deprecated
public void sendStickyOrderedBroadcastAsUser(Intent intent,
- UserHandle user, BroadcastReceiver resultReceiver,
- Handler scheduler, int initialCode, String initialData,
- Bundle initialExtras) {
+ UserHandle user, @Nullable BroadcastReceiver resultReceiver,
+ @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+ @Nullable Bundle initialExtras) {
mBase.sendStickyOrderedBroadcastAsUser(intent, user, resultReceiver,
scheduler, initialCode, initialData, initialExtras);
}
@@ -666,29 +670,26 @@ public class ContextWrapper extends Context {
}
@Override
- public Intent registerReceiver(
- BroadcastReceiver receiver, IntentFilter filter) {
+ public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
@Override
- public Intent registerReceiver(
- BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter,
+ int flags) {
return mBase.registerReceiver(receiver, filter, flags);
}
@Override
- public Intent registerReceiver(
- BroadcastReceiver receiver, IntentFilter filter,
- String broadcastPermission, Handler scheduler) {
+ public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter,
+ @Nullable String broadcastPermission, @Nullable Handler scheduler) {
return mBase.registerReceiver(receiver, filter, broadcastPermission,
scheduler);
}
@Override
- public Intent registerReceiver(
- BroadcastReceiver receiver, IntentFilter filter,
- String broadcastPermission, Handler scheduler, int flags) {
+ public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter,
+ @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags) {
return mBase.registerReceiver(receiver, filter, broadcastPermission,
scheduler, flags);
}
@@ -706,9 +707,9 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
@UnsupportedAppUsage
- public Intent registerReceiverAsUser(
- BroadcastReceiver receiver, UserHandle user, IntentFilter filter,
- String broadcastPermission, Handler scheduler) {
+ public Intent registerReceiverAsUser(@Nullable BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, @Nullable String broadcastPermission,
+ @Nullable Handler scheduler) {
return mBase.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
scheduler);
}
@@ -719,12 +720,12 @@ public class ContextWrapper extends Context {
}
@Override
- public ComponentName startService(Intent service) {
+ public @Nullable ComponentName startService(Intent service) {
return mBase.startService(service);
}
@Override
- public ComponentName startForegroundService(Intent service) {
+ public @Nullable ComponentName startForegroundService(Intent service) {
return mBase.startForegroundService(service);
}
@@ -736,14 +737,14 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
@UnsupportedAppUsage
- public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+ public @Nullable ComponentName startServiceAsUser(Intent service, UserHandle user) {
return mBase.startServiceAsUser(service, user);
}
/** @hide */
@Override
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
+ public @Nullable ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
return mBase.startForegroundServiceAsUser(service, user);
}
@@ -797,12 +798,12 @@ public class ContextWrapper extends Context {
@Override
public boolean startInstrumentation(ComponentName className,
- String profileFile, Bundle arguments) {
+ @Nullable String profileFile, @Nullable Bundle arguments) {
return mBase.startInstrumentation(className, profileFile, arguments);
}
@Override
- public Object getSystemService(String name) {
+ public @Nullable Object getSystemService(String name) {
return mBase.getSystemService(name);
}
@@ -839,18 +840,18 @@ public class ContextWrapper extends Context {
@Override
public void enforcePermission(
- String permission, int pid, int uid, String message) {
+ String permission, int pid, int uid, @Nullable String message) {
mBase.enforcePermission(permission, pid, uid, message);
}
@Override
- public void enforceCallingPermission(String permission, String message) {
+ public void enforceCallingPermission(String permission, @Nullable String message) {
mBase.enforceCallingPermission(permission, message);
}
@Override
public void enforceCallingOrSelfPermission(
- String permission, String message) {
+ String permission, @Nullable String message) {
mBase.enforceCallingOrSelfPermission(permission, message);
}
@@ -891,8 +892,8 @@ public class ContextWrapper extends Context {
}
@Override
- public int checkUriPermission(Uri uri, String readPermission,
- String writePermission, int pid, int uid, int modeFlags) {
+ public int checkUriPermission(@Nullable Uri uri, @Nullable String readPermission,
+ @Nullable String writePermission, int pid, int uid, int modeFlags) {
return mBase.checkUriPermission(uri, readPermission, writePermission,
pid, uid, modeFlags);
}
@@ -917,8 +918,8 @@ public class ContextWrapper extends Context {
@Override
public void enforceUriPermission(
- Uri uri, String readPermission, String writePermission,
- int pid, int uid, int modeFlags, String message) {
+ @Nullable Uri uri, @Nullable String readPermission, @Nullable String writePermission,
+ int pid, int uid, int modeFlags, @Nullable String message) {
mBase.enforceUriPermission(
uri, readPermission, writePermission, pid, uid, modeFlags,
message);
@@ -1063,7 +1064,7 @@ public class ContextWrapper extends Context {
* @hide
*/
@Override
- public IBinder getActivityToken() {
+ public @Nullable IBinder getActivityToken() {
return mBase.getActivityToken();
}
@@ -1071,7 +1072,7 @@ public class ContextWrapper extends Context {
* @hide
*/
@Override
- public IBinder getWindowContextToken() {
+ public @Nullable IBinder getWindowContextToken() {
return mBase != null ? mBase.getWindowContextToken() : null;
}
@@ -1079,8 +1080,8 @@ public class ContextWrapper extends Context {
* @hide
*/
@Override
- public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
- int flags) {
+ public @Nullable IServiceConnection getServiceDispatcher(ServiceConnection conn,
+ Handler handler, int flags) {
return mBase.getServiceDispatcher(conn, handler, flags);
}
@@ -1142,7 +1143,7 @@ public class ContextWrapper extends Context {
* @hide
*/
@Override
- public ContentCaptureOptions getContentCaptureOptions() {
+ public @Nullable ContentCaptureOptions getContentCaptureOptions() {
return mBase == null ? null : mBase.getContentCaptureOptions();
}
@@ -1151,7 +1152,7 @@ public class ContextWrapper extends Context {
*/
@TestApi
@Override
- public void setContentCaptureOptions(ContentCaptureOptions options) {
+ public void setContentCaptureOptions(@Nullable ContentCaptureOptions options) {
if (mBase != null) {
mBase.setContentCaptureOptions(options);
}
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
index 58445a7f9242..495f94f98f95 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -25,12 +25,12 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.annotations.GuardedBy;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -142,12 +142,12 @@ public class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType>
}
static class MySerializer implements XmlSerializerAndParser<SyncAdapterType> {
- public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException {
+ public void writeAsXml(SyncAdapterType item, TypedXmlSerializer out) throws IOException {
out.attribute(null, "authority", item.authority);
out.attribute(null, "accountType", item.accountType);
}
- public SyncAdapterType createFromXml(XmlPullParser parser)
+ public SyncAdapterType createFromXml(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
final String authority = parser.getAttributeValue(null, "authority");
final String accountType = parser.getAttributeValue(null, "accountType");
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 6ccbc36e26f6..9a73be9d44a4 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -33,6 +33,7 @@ interface IPackageInstallerSession {
ParcelFileDescriptor openRead(String name);
void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
+ void stageViaHardLink(String target);
void addChecksums(String name, in Checksum[] checksums);
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index 0a913acba9f5..56b8bd80a06a 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -16,11 +16,11 @@
package android.content.pm;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import android.annotation.SystemApi;
import android.os.Parcel;
@@ -28,12 +28,13 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -72,7 +73,7 @@ public final class IntentFilterVerificationInfo implements Parcelable {
}
/** @hide */
- public IntentFilterVerificationInfo(XmlPullParser parser)
+ public IntentFilterVerificationInfo(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
readFromXml(parser);
}
@@ -121,7 +122,7 @@ public final class IntentFilterVerificationInfo implements Parcelable {
return sb.toString();
}
- String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
+ String getStringFromXml(TypedXmlPullParser parser, String attribute, String defaultValue) {
String value = parser.getAttributeValue(null, attribute);
if (value == null) {
String msg = "Missing element under " + TAG +": " + attribute + " at " +
@@ -133,20 +134,12 @@ public final class IntentFilterVerificationInfo implements Parcelable {
}
}
- int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
- String value = parser.getAttributeValue(null, attribute);
- if (TextUtils.isEmpty(value)) {
- String msg = "Missing element under " + TAG +": " + attribute + " at " +
- parser.getPositionDescription();
- Log.w(TAG, msg);
- return defaultValue;
- } else {
- return Integer.parseInt(value);
- }
+ int getIntFromXml(TypedXmlPullParser parser, String attribute, int defaultValue) {
+ return parser.getAttributeInt(null, attribute, defaultValue);
}
/** @hide */
- public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
+ public void readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
IOException {
mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
if (mPackageName == null) {
@@ -182,9 +175,9 @@ public final class IntentFilterVerificationInfo implements Parcelable {
}
/** @hide */
- public void writeToXml(XmlSerializer serializer) throws IOException {
+ public void writeToXml(TypedXmlSerializer serializer) throws IOException {
serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
- serializer.attribute(null, ATTR_STATUS, String.valueOf(mStatus));
+ serializer.attributeInt(null, ATTR_STATUS, mStatus);
for (String str : mDomains) {
serializer.startTag(null, TAG_DOMAIN);
serializer.attribute(null, ATTR_DOMAIN_NAME, str);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 0dcfd38294b9..d4a98f82a4f7 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1049,6 +1049,31 @@ public class PackageInstaller {
}
/**
+ * Populate an APK file by creating a hard link to avoid the need to copy.
+ * <p>
+ * Note this API is used by RollbackManager only and can only be called from system_server.
+ * {@code target} will be relabeled if link is created successfully. RollbackManager has
+ * to delete {@code target} when the session is committed successfully to avoid SELinux
+ * label conflicts.
+ * <p>
+ * Note No more bytes should be written to the file once the link is created successfully.
+ *
+ * @param target the path of the link target
+ *
+ * @hide
+ */
+ public void stageViaHardLink(String target) throws IOException {
+ try {
+ mSession.stageViaHardLink(target);
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Ensure that any outstanding data for given stream has been committed
* to disk. This is only valid for streams returned from
* {@link #openWrite(String, long, long)}.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 044b3b2e8284..d550c7c68033 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -98,6 +98,11 @@ import java.util.Set;
* packages that are currently installed on the device.
*
* You can find this class through {@link Context#getPackageManager}.
+ *
+ * <p class="note"><strong>Note: </strong>If your app targets Android 11 (API level 30) or
+ * higher, the methods in this class each return a filtered list of apps. Learn more about how to
+ * <a href="/training/basics/intents/package-visibility">manage package visibility</a>.
+ * </p>
*/
public abstract class PackageManager {
private static final String TAG = "PackageManager";
@@ -2810,6 +2815,15 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device is capable of communicating with
+ * other devices via ultra wideband.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_UWB = "android.hardware.uwb";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports connecting to USB devices
* as the USB host.
*/
@@ -4117,6 +4131,14 @@ public abstract class PackageManager {
*/
public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2;
+ /**
+ * A manifest property to control app's participation in {@code adb backup}. Should only
+ * be used by system / privileged apps.
+ *
+ * @hide
+ */
+ public static final String PROPERTY_ALLOW_ADB_BACKUP = "android.backup.ALLOW_ADB_BACKUP";
+
/** {@hide} */
public int getUserId() {
return UserHandle.myUserId();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index cb0decfa4982..4dfbd75a9d67 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -406,9 +406,15 @@ public class PackageParser {
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
- public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
- boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
- String[] splitCodePaths, int[] splitRevisionCodes) {
+ // This does not represent the actual manifest structure since the 'profilable' tag
+ // could be used with attributes other than 'shell'. Extend if necessary.
+ public final boolean profilableByShell;
+ public final boolean isSplitRequired;
+ public final boolean useEmbeddedDex;
+
+ public PackageLite(String codePath, String baseCodePath, ApkLite baseApk,
+ String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames,
+ String[] configForSplit, String[] splitCodePaths, int[] splitRevisionCodes) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.versionCodeMajor = baseApk.versionCodeMajor;
@@ -418,8 +424,10 @@ public class PackageParser {
this.isFeatureSplits = isFeatureSplits;
this.usesSplitNames = usesSplitNames;
this.configForSplit = configForSplit;
+ // The following paths may be different from the path in ApkLite because we
+ // move or rename the APK files. Use parameters to indicate the correct paths.
this.codePath = codePath;
- this.baseCodePath = baseApk.codePath;
+ this.baseCodePath = baseCodePath;
this.splitCodePaths = splitCodePaths;
this.baseRevisionCode = baseApk.revisionCode;
this.splitRevisionCodes = splitRevisionCodes;
@@ -429,6 +437,9 @@ public class PackageParser {
this.use32bitAbi = baseApk.use32bitAbi;
this.extractNativeLibs = baseApk.extractNativeLibs;
this.isolatedSplits = baseApk.isolatedSplits;
+ this.useEmbeddedDex = baseApk.useEmbeddedDex;
+ this.isSplitRequired = baseApk.isSplitRequired;
+ this.profilableByShell = baseApk.profilableByShell;
}
public List<String> getAllCodePaths() {
@@ -439,6 +450,10 @@ public class PackageParser {
}
return paths;
}
+
+ public long getLongVersionCode() {
+ return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+ }
}
/**
@@ -941,7 +956,8 @@ public class PackageParser {
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
+ return new PackageLite(packagePath, baseApk.codePath, baseApk, null, null, null, null, null,
+ null);
}
static PackageLite parseClusterPackageLite(File packageDir, int flags)
@@ -1031,8 +1047,8 @@ public class PackageParser {
}
final String codePath = packageDir.getAbsolutePath();
- return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
- configForSplits, splitCodePaths, splitRevisionCodes);
+ return new PackageLite(codePath, baseApk.codePath, baseApk, splitNames, isFeatureSplits,
+ usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes);
}
/**
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 2fca980e764c..99258712030c 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -41,6 +41,8 @@ import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -560,7 +562,7 @@ public class PackageUserState {
* @param out the {@link XmlSerializer} object
* @throws IOException
*/
- public void saveToXml(XmlSerializer out) throws IOException {
+ public void saveToXml(TypedXmlSerializer out) throws IOException {
if (dialogInfo != null) {
out.startTag(null, TAG_DIALOG_INFO);
dialogInfo.saveToXml(out);
@@ -594,7 +596,7 @@ public class PackageUserState {
* @param in the reader
* @return
*/
- public static SuspendParams restoreFromXml(XmlPullParser in) throws IOException {
+ public static SuspendParams restoreFromXml(TypedXmlPullParser in) throws IOException {
SuspendDialogInfo readDialogInfo = null;
PersistableBundle readAppExtras = null;
PersistableBundle readLauncherExtras = null;
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 192470e964e0..7ecb11248d23 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -36,21 +36,21 @@ import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
+
+import libcore.io.IoUtils;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileDescriptor;
@@ -58,7 +58,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -672,8 +671,7 @@ public abstract class RegisteredServicesCache<V> {
*/
private void readPersistentServicesLocked(InputStream is)
throws XmlPullParserException, IOException {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(is, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(is);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG
&& eventType != XmlPullParser.END_DOCUMENT) {
@@ -690,8 +688,7 @@ public abstract class RegisteredServicesCache<V> {
if (service == null) {
break;
}
- String uidString = parser.getAttributeValue(null, "uid");
- final int uid = Integer.parseInt(uidString);
+ final int uid = parser.getAttributeInt(null, "uid");
final int userId = UserHandle.getUserId(uid);
final UserServices<V> user = findOrCreateUserLocked(userId,
false /*loadFromFileIfNew*/) ;
@@ -762,14 +759,13 @@ public abstract class RegisteredServicesCache<V> {
FileOutputStream fos = null;
try {
fos = atomicFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "services");
for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
out.startTag(null, "service");
- out.attribute(null, "uid", Integer.toString(service.getValue()));
+ out.attributeInt(null, "uid", service.getValue());
mSerializerAndParser.writeAsXml(service.getKey(), out);
out.endTag(null, "service");
}
diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java
index 851a08116f56..60f321883e98 100644
--- a/core/java/android/content/pm/SuspendDialogInfo.java
+++ b/core/java/android/content/pm/SuspendDialogInfo.java
@@ -29,13 +29,12 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -169,38 +168,38 @@ public final class SuspendDialogInfo implements Parcelable {
/**
* @hide
*/
- public void saveToXml(XmlSerializer out) throws IOException {
+ public void saveToXml(TypedXmlSerializer out) throws IOException {
if (mIconResId != ID_NULL) {
- XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId);
+ out.attributeInt(null, XML_ATTR_ICON_RES_ID, mIconResId);
}
if (mTitleResId != ID_NULL) {
- XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId);
+ out.attributeInt(null, XML_ATTR_TITLE_RES_ID, mTitleResId);
}
if (mDialogMessageResId != ID_NULL) {
- XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
+ out.attributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
} else {
XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage);
}
if (mNeutralButtonTextResId != ID_NULL) {
- XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
+ out.attributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
}
- XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction);
+ out.attributeInt(null, XML_ATTR_BUTTON_ACTION, mNeutralButtonAction);
}
/**
* @hide
*/
- public static SuspendDialogInfo restoreFromXml(XmlPullParser in) {
+ public static SuspendDialogInfo restoreFromXml(TypedXmlPullParser in) {
final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder();
try {
- final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL);
- final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL);
- final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID,
- ID_NULL);
- final int buttonAction = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_ACTION,
- BUTTON_ACTION_MORE_DETAILS);
- final int dialogMessageResId = XmlUtils.readIntAttribute(
- in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
+ final int iconId = in.getAttributeInt(null, XML_ATTR_ICON_RES_ID, ID_NULL);
+ final int titleId = in.getAttributeInt(null, XML_ATTR_TITLE_RES_ID, ID_NULL);
+ final int buttonTextId =
+ in.getAttributeInt(null, XML_ATTR_BUTTON_TEXT_RES_ID, ID_NULL);
+ final int buttonAction =
+ in.getAttributeInt(null, XML_ATTR_BUTTON_ACTION, BUTTON_ACTION_MORE_DETAILS);
+ final int dialogMessageResId =
+ in.getAttributeInt(null, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE);
if (iconId != ID_NULL) {
diff --git a/core/java/android/content/pm/XmlSerializerAndParser.java b/core/java/android/content/pm/XmlSerializerAndParser.java
index 5dce83902f78..51cd6ca60f59 100644
--- a/core/java/android/content/pm/XmlSerializerAndParser.java
+++ b/core/java/android/content/pm/XmlSerializerAndParser.java
@@ -17,6 +17,10 @@
package android.content.pm;
import android.compat.annotation.UnsupportedAppUsage;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -26,8 +30,16 @@ import java.io.IOException;
/** @hide */
public interface XmlSerializerAndParser<T> {
+ void writeAsXml(T item, TypedXmlSerializer out) throws IOException;
+ T createFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException;
+
@UnsupportedAppUsage
- void writeAsXml(T item, XmlSerializer out) throws IOException;
+ default void writeAsXml(T item, XmlSerializer out) throws IOException {
+ writeAsXml(item, XmlUtils.makeTyped(out));
+ }
+
@UnsupportedAppUsage
- T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException;
+ default T createFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+ return createFromXml(XmlUtils.makeTyped(parser));
+ }
}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 72a66ed4d9fe..f583e2597855 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -18,9 +18,11 @@ package android.content.pm.parsing;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.content.pm.parsing.ParsingPackageUtils.validateName;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import android.annotation.NonNull;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -50,6 +52,7 @@ import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/** @hide */
public class ApkLiteParseUtils {
@@ -95,8 +98,8 @@ public class ApkLiteParseUtils {
final PackageParser.ApkLite baseApk = result.getResult();
final String packagePath = packageFile.getAbsolutePath();
return input.success(
- new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null,
- null, null));
+ new PackageParser.PackageLite(packagePath, baseApk.codePath, baseApk, null,
+ null, null, null, null, null));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -159,13 +162,43 @@ public class ApkLiteParseUtils {
}
final PackageParser.ApkLite baseApk = apks.remove(null);
+ return composePackageLiteFromApks(input, packageDir, baseApk, apks);
+ }
+
+ /**
+ * Utility method that retrieves lightweight details about the package by given location,
+ * base APK, and split APKs.
+ *
+ * @param packageDir Path to the package
+ * @param baseApk Parsed base APK
+ * @param splitApks Parsed split APKs
+ * @return PackageLite
+ */
+ public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks(
+ ParseInput input, File packageDir, PackageParser.ApkLite baseApk,
+ ArrayMap<String, PackageParser.ApkLite> splitApks) {
+ return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false);
+ }
+
+ /**
+ * Utility method that retrieves lightweight details about the package by given location,
+ * base APK, and split APKs.
+ *
+ * @param packageDir Path to the package
+ * @param baseApk Parsed base APK
+ * @param splitApks Parsed split APKs
+ * @param apkRenamed Indicate whether the APKs are renamed after parsed.
+ * @return PackageLite
+ */
+ public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks(
+ ParseInput input, File packageDir, PackageParser.ApkLite baseApk,
+ ArrayMap<String, PackageParser.ApkLite> splitApks, boolean apkRenamed) {
if (baseApk == null) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Missing base APK in " + packageDir);
}
-
// Always apply deterministic ordering based on splitName
- final int size = apks.size();
+ final int size = ArrayUtils.size(splitApks);
String[] splitNames = null;
boolean[] isFeatureSplits = null;
@@ -181,23 +214,39 @@ public class ApkLiteParseUtils {
splitCodePaths = new String[size];
splitRevisionCodes = new int[size];
- splitNames = apks.keySet().toArray(splitNames);
+ splitNames = splitApks.keySet().toArray(splitNames);
Arrays.sort(splitNames, PackageParser.sSplitNameComparator);
for (int i = 0; i < size; i++) {
- final PackageParser.ApkLite apk = apks.get(splitNames[i]);
+ final PackageParser.ApkLite apk = splitApks.get(splitNames[i]);
usesSplitNames[i] = apk.usesSplitName;
isFeatureSplits[i] = apk.isFeatureSplit;
configForSplits[i] = apk.configForSplit;
- splitCodePaths[i] = apk.codePath;
+ splitCodePaths[i] = apkRenamed ? new File(packageDir,
+ splitNameToFileName(apk)).getAbsolutePath() : apk.codePath;
splitRevisionCodes[i] = apk.revisionCode;
}
}
final String codePath = packageDir.getAbsolutePath();
- return input.success(new PackageParser.PackageLite(codePath, baseApk, splitNames,
- isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths,
- splitRevisionCodes));
+ final String baseCodePath = apkRenamed ? new File(packageDir,
+ splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.codePath;
+ return input.success(
+ new PackageParser.PackageLite(codePath, baseCodePath, baseApk, splitNames,
+ isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths,
+ splitRevisionCodes));
+ }
+
+ /**
+ * Utility method that retrieves canonical file name by given split name from parsed APK.
+ *
+ * @param apk Parsed APK
+ * @return The canonical file name
+ */
+ public static String splitNameToFileName(@NonNull PackageParser.ApkLite apk) {
+ Objects.requireNonNull(apk);
+ final String fileName = apk.splitName == null ? "base" : "split_" + apk.splitName;
+ return fileName + APK_FILE_EXTENSION;
}
/**
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index cb43943f4864..cc12125c13f0 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -36,7 +36,7 @@ interface IBiometricAuthenticator {
// Retrieve static sensor properties
SensorPropertiesInternal getSensorProperties(String opPackageName);
- // Requests a proto dump of the service. See biometrics.proto
+ // Requests a proto dump of the sensor. See biometrics.proto
byte[] dumpSensorServiceStateProto();
// This method prepares the service to start authenticating, but doesn't start authentication.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 232056cb13d4..a0c12235dea9 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2772,7 +2772,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* </ol>
* </li>
* <li>Setting {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} to values different than 1.0 and
- * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be windowboxing at the same time is undefined behavior.</li>
+ * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be windowboxing at the same time are not supported. In this
+ * case, the camera framework will override the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be the active
+ * array.</li>
* </ul>
* <p>LEGACY capability devices will only support CENTER_ONLY cropping.</p>
* <p><b>Possible values:</b>
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index d931789a7005..d85bcbd15e50 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2243,7 +2243,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p>
* <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}
* must only be used for letterboxing or pillarboxing of the sensor active array, and no
- * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p>
+ * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0. If
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is not 1.0, and {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is set to be
+ * windowboxing, the camera framework will override the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be
+ * the active array.</p>
* <p><b>Range of valid values:</b><br>
* {@link CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE android.control.zoomRatioRange}</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index cd69788f1924..4424a71ae837 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2490,7 +2490,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* explicitly set {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, its value defaults to 1.0.</p>
* <p>One limitation of controlling zoom using zoomRatio is that the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}
* must only be used for letterboxing or pillarboxing of the sensor active array, and no
- * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0.</p>
+ * FREEFORM cropping can be used with {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} other than 1.0. If
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is not 1.0, and {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is set to be
+ * windowboxing, the camera framework will override the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to be
+ * the active array.</p>
* <p><b>Range of valid values:</b><br>
* {@link CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE android.control.zoomRatioRange}</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index d9c1063cd39d..366734e9bf11 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -24,13 +24,13 @@ import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -343,15 +343,15 @@ public final class BrightnessConfiguration implements Parcelable {
*
* @hide
*/
- public void saveToXml(@NonNull XmlSerializer serializer) throws IOException {
+ public void saveToXml(@NonNull TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
if (mDescription != null) {
serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
}
for (int i = 0; i < mLux.length; i++) {
serializer.startTag(null, TAG_BRIGHTNESS_POINT);
- serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
- serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
+ serializer.attributeFloat(null, ATTR_LUX, mLux[i]);
+ serializer.attributeFloat(null, ATTR_NITS, mNits[i]);
serializer.endTag(null, TAG_BRIGHTNESS_POINT);
}
serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
@@ -370,7 +370,7 @@ public final class BrightnessConfiguration implements Parcelable {
final int category = entry.getKey();
final BrightnessCorrection correction = entry.getValue();
serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
- serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
+ serializer.attributeInt(null, ATTR_CATEGORY, category);
correction.saveToXml(serializer);
serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
}
@@ -378,19 +378,18 @@ public final class BrightnessConfiguration implements Parcelable {
serializer.startTag(null, TAG_BRIGHTNESS_PARAMS);
if (mShouldCollectColorSamples) {
- serializer.attribute(null, ATTR_COLLECT_COLOR, Boolean.toString(true));
+ serializer.attributeBoolean(null, ATTR_COLLECT_COLOR, true);
}
if (mShortTermModelTimeout >= 0) {
- serializer.attribute(null, ATTR_MODEL_TIMEOUT,
- Long.toString(mShortTermModelTimeout));
+ serializer.attributeLong(null, ATTR_MODEL_TIMEOUT, mShortTermModelTimeout);
}
if (!Float.isNaN(mShortTermModelLowerLuxMultiplier)) {
- serializer.attribute(null, ATTR_MODEL_LOWER_BOUND,
- Float.toString(mShortTermModelLowerLuxMultiplier));
+ serializer.attributeFloat(null, ATTR_MODEL_LOWER_BOUND,
+ mShortTermModelLowerLuxMultiplier);
}
if (!Float.isNaN(mShortTermModelUpperLuxMultiplier)) {
- serializer.attribute(null, ATTR_MODEL_UPPER_BOUND,
- Float.toString(mShortTermModelUpperLuxMultiplier));
+ serializer.attributeFloat(null, ATTR_MODEL_UPPER_BOUND,
+ mShortTermModelUpperLuxMultiplier);
}
serializer.endTag(null, TAG_BRIGHTNESS_PARAMS);
}
@@ -408,7 +407,7 @@ public final class BrightnessConfiguration implements Parcelable {
*
* @hide
*/
- public static BrightnessConfiguration loadFromXml(@NonNull XmlPullParser parser)
+ public static BrightnessConfiguration loadFromXml(@NonNull TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
String description = null;
List<Float> luxList = new ArrayList<>();
@@ -440,22 +439,17 @@ public final class BrightnessConfiguration implements Parcelable {
continue;
}
final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
- final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
+ final int category = parser.getAttributeInt(null, ATTR_CATEGORY, -1);
BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
if (packageName != null) {
correctionsByPackageName.put(packageName, correction);
- } else if (categoryText != null) {
- try {
- final int category = Integer.parseInt(categoryText);
- correctionsByCategory.put(category, correction);
- } catch (NullPointerException | NumberFormatException e) {
- continue;
- }
+ } else if (category != -1) {
+ correctionsByCategory.put(category, correction);
}
}
} else if (TAG_BRIGHTNESS_PARAMS.equals(parser.getName())) {
shouldCollectColorSamples =
- Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_COLLECT_COLOR));
+ parser.getAttributeBoolean(null, ATTR_COLLECT_COLOR, false);
Long timeout = loadLongFromXml(parser, ATTR_MODEL_TIMEOUT);
if (timeout != null) {
shortTermModelTimeout = timeout;
@@ -491,23 +485,16 @@ public final class BrightnessConfiguration implements Parcelable {
return builder.build();
}
- private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
- final String string = parser.getAttributeValue(null, attribute);
- try {
- return Float.parseFloat(string);
- } catch (NullPointerException | NumberFormatException e) {
- return Float.NaN;
- }
+ private static float loadFloatFromXml(TypedXmlPullParser parser, String attribute) {
+ return parser.getAttributeFloat(null, attribute, Float.NaN);
}
- private static Long loadLongFromXml(XmlPullParser parser, String attribute) {
- final String string = parser.getAttributeValue(null, attribute);
+ private static Long loadLongFromXml(TypedXmlPullParser parser, String attribute) {
try {
- return Long.parseLong(string);
- } catch (NullPointerException | NumberFormatException e) {
- // Ignoring
+ return parser.getAttributeLong(null, attribute);
+ } catch (Exception e) {
+ return null;
}
- return null;
}
/**
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
index bbfc45edb89a..2919ec3fbf08 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.java
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -23,12 +23,12 @@ import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.MathUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
@@ -153,7 +153,7 @@ public final class BrightnessCorrection implements Parcelable {
*
* @hide
*/
- public void saveToXml(XmlSerializer serializer) throws IOException {
+ public void saveToXml(TypedXmlSerializer serializer) throws IOException {
mImplementation.saveToXml(serializer);
}
@@ -170,7 +170,7 @@ public final class BrightnessCorrection implements Parcelable {
*
* @hide
*/
- public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ public static BrightnessCorrection loadFromXml(TypedXmlPullParser parser) throws IOException,
XmlPullParserException {
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
@@ -181,20 +181,15 @@ public final class BrightnessCorrection implements Parcelable {
return null;
}
- private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
- final String string = parser.getAttributeValue(null, attribute);
- try {
- return Float.parseFloat(string);
- } catch (NullPointerException | NumberFormatException e) {
- return Float.NaN;
- }
+ private static float loadFloatFromXml(TypedXmlPullParser parser, String attribute) {
+ return parser.getAttributeFloat(null, attribute, Float.NaN);
}
private interface BrightnessCorrectionImplementation {
float apply(float brightness);
String toString();
void writeToParcel(Parcel dest);
- void saveToXml(XmlSerializer serializer) throws IOException;
+ void saveToXml(TypedXmlSerializer serializer) throws IOException;
// Package-private static methods:
// static BrightnessCorrection readFromParcel(Parcel in);
// static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
@@ -263,10 +258,10 @@ public final class BrightnessCorrection implements Parcelable {
}
@Override
- public void saveToXml(XmlSerializer serializer) throws IOException {
+ public void saveToXml(TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
- serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
- serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
+ serializer.attributeFloat(null, ATTR_SCALE, mScale);
+ serializer.attributeFloat(null, ATTR_TRANSLATE, mTranslate);
serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
}
@@ -276,7 +271,7 @@ public final class BrightnessCorrection implements Parcelable {
return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
}
- static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ static BrightnessCorrection loadFromXml(TypedXmlPullParser parser) throws IOException,
XmlPullParserException {
final float scale = loadFloatFromXml(parser, ATTR_SCALE);
final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index ca5eeb1863c7..9bae1ff4b906 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -19,6 +19,7 @@ package android.hardware.display;
import static android.view.Display.DEFAULT_DISPLAY;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -38,9 +39,12 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.Surface;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+
/**
* Manages the properties of attached displays.
*/
@@ -336,6 +340,40 @@ public final class DisplayManager {
*/
public static final int VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP = 1 << 11;
+
+ /** @hide */
+ @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
+ SWITCHING_TYPE_NONE,
+ SWITCHING_TYPE_WITHIN_GROUPS,
+ SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SwitchingType {}
+
+ /**
+ * No mode switching will happen.
+ * @hide
+ */
+ @TestApi
+ public static final int SWITCHING_TYPE_NONE = 0;
+
+ /**
+ * Allow only refresh rate switching between modes in the same configuration group. This way
+ * only switches without visual interruptions for the user will be allowed.
+ * @hide
+ */
+ @TestApi
+ public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1;
+
+ /**
+ * Allow refresh rate switching between all refresh rates even if the switch with have visual
+ * interruptions for the user.
+ * @hide
+ */
+ @TestApi
+ public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
+
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
@@ -875,6 +913,29 @@ public final class DisplayManager {
}
/**
+ * Sets the refresh rate switching type.
+ * This matches {@link android.provider.Settings.Secure.MATCH_CONTENT_FRAME_RATE}
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
+ public void setRefreshRateSwitchingType(@SwitchingType int newValue) {
+ mGlobal.setRefreshRateSwitchingType(newValue);
+ }
+
+ /**
+ * Returns the refresh rate switching type.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
+ @SwitchingType public int getRefreshRateSwitchingType() {
+ return mGlobal.getRefreshRateSwitchingType();
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 7b4889f0a1b3..77ae9471dfb0 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -725,6 +725,33 @@ public final class DisplayManagerGlobal {
}
}
+ /**
+ * Sets the refresh rate switching type.
+ *
+ * @hide
+ */
+ public void setRefreshRateSwitchingType(@DisplayManager.SwitchingType int newValue) {
+ try {
+ mDm.setRefreshRateSwitchingType(newValue);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the refresh rate switching type.
+ *
+ * @hide
+ */
+ @DisplayManager.SwitchingType
+ public int getRefreshRateSwitchingType() {
+ try {
+ return mDm.getRefreshRateSwitchingType();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 85da6424377a..a9f78fa03a6d 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -134,4 +134,10 @@ interface IDisplayManager {
// battery etc.
void setShouldAlwaysRespectAppRequestedMode(boolean enabled);
boolean shouldAlwaysRespectAppRequestedMode();
+
+ // Sets the refresh rate switching type.
+ void setRefreshRateSwitchingType(int newValue);
+
+ // Returns the refresh rate switching type.
+ int getRefreshRateSwitchingType();
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index c5c51e4661c5..468157a19971 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -33,8 +33,8 @@ interface IFaceService {
// Creates a test session with the specified sensorId
ITestSession createTestSession(int sensorId, String opPackageName);
- // Requests a proto dump of the service to the specified fd
- byte[] dumpSensorServiceStateProto();
+ // Requests a proto dump of the specified sensor
+ byte[] dumpSensorServiceStateProto(int sensorId);
// Retrieve static sensor properties for all face sensors
List<FaceSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index a4ce29ba4ea0..64abbea12de0 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -34,8 +34,8 @@ interface IFingerprintService {
// Creates a test session with the specified sensorId
ITestSession createTestSession(int sensorId, String opPackageName);
- // Requests a proto dump of the service to the specified fd
- byte[] dumpSensorServiceStateProto();
+ // Requests a proto dump of the specified sensor
+ byte[] dumpSensorServiceStateProto(int sensorId);
// Retrieve static sensor properties for all fingerprint sensors
List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index eef4089ac336..ae10f4006ce5 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -367,35 +367,35 @@ public final class HdmiControlManager {
@Retention(RetentionPolicy.SOURCE)
public @interface HdmiCecVersion {}
- // -- Which devices the playback device can send a <Standby> message to upon going to sleep.
+ // -- Scope of CEC power control messages sent by a playback device.
/**
- * Send <Standby> to TV only.
+ * Send CEC power control messages to TV only.
*
* @hide
*/
- public static final String SEND_STANDBY_ON_SLEEP_TO_TV = "to_tv";
+ public static final String POWER_CONTROL_MODE_TV = "to_tv";
/**
- * Broadcast <Standby> to all devices in the network.
+ * Broadcast CEC power control messages to all devices in the network.
*
* @hide
*/
- public static final String SEND_STANDBY_ON_SLEEP_BROADCAST = "broadcast";
+ public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
/**
- * Don't send any <Standby> message.
+ * Don't send any CEC power control messages.
*
* @hide
*/
- public static final String SEND_STANDBY_ON_SLEEP_NONE = "none";
+ public static final String POWER_CONTROL_MODE_NONE = "none";
/**
* @hide
*/
@StringDef({
- SEND_STANDBY_ON_SLEEP_TO_TV,
- SEND_STANDBY_ON_SLEEP_BROADCAST,
- SEND_STANDBY_ON_SLEEP_NONE
+ POWER_CONTROL_MODE_TV,
+ POWER_CONTROL_MODE_BROADCAST,
+ POWER_CONTROL_MODE_NONE
})
@Retention(RetentionPolicy.SOURCE)
- public @interface StandbyBehavior {}
+ public @interface PowerControlMode {}
// -- Which power state action should be taken when Active Source is lost.
/**
@@ -457,11 +457,11 @@ public final class HdmiControlManager {
*/
public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
/**
- * Name of a setting deciding on the Standby message behaviour on sleep.
+ * Name of a setting deciding on the power control mode.
*
* @hide
*/
- public static final String CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP = "send_standby_on_sleep";
+ public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "send_standby_on_sleep";
/**
* Name of a setting deciding on power state action when losing Active Source.
*
@@ -482,7 +482,7 @@ public final class HdmiControlManager {
@StringDef({
CEC_SETTING_NAME_HDMI_CEC_ENABLED,
CEC_SETTING_NAME_HDMI_CEC_VERSION,
- CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ CEC_SETTING_NAME_POWER_CONTROL_MODE,
CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
})
@@ -1506,7 +1506,7 @@ public final class HdmiControlManager {
}
/**
- * Set the 'send_standby_on_sleep' option.
+ * Set the 'power_control_mode' option.
*
* @param value the desired value
* @throws IllegalArgumentException when the new value is not allowed.
@@ -1515,20 +1515,20 @@ public final class HdmiControlManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
- public void setSendStandbyOnSleep(@NonNull @StandbyBehavior String value) {
+ public void setPowerControlMode(@NonNull @PowerControlMode String value) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
throw new RuntimeException("HdmiControlService is not available");
}
try {
- mService.setCecSettingStringValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, value);
+ mService.setCecSettingStringValue(CEC_SETTING_NAME_POWER_CONTROL_MODE, value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Get the value of 'send_standby_on_sleep' option.
+ * Get the value of 'power_control_mode' option.
*
* @return the current value.
* @throws RuntimeException when the HdmiControlService is not available.
@@ -1536,15 +1536,15 @@ public final class HdmiControlManager {
* @hide
*/
@NonNull
- @StandbyBehavior
+ @PowerControlMode
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
- public String getSendStandbyOnSleep() {
+ public String getPowerControlMode() {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
throw new RuntimeException("HdmiControlService is not available");
}
try {
- return mService.getCecSettingStringValue(CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ return mService.getCecSettingStringValue(CEC_SETTING_NAME_POWER_CONTROL_MODE);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index aac57fe88b17..c28bab7f643f 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -609,7 +609,7 @@ public class UsbManager {
public @interface UsbFunctionMode {}
/** @hide */
- @IntDef(flag = true, prefix = { "GADGET_HAL_" }, value = {
+ @IntDef(prefix = { "GADGET_HAL_" }, value = {
GADGET_HAL_NOT_SUPPORTED,
GADGET_HAL_V1_0,
GADGET_HAL_V1_1,
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 43c418e2cb26..bb7aff651b3d 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -202,7 +202,7 @@ public final class UsbPortStatus implements Parcelable {
public static final int CONTAMINANT_PROTECTION_DISABLED =
android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED;
- @IntDef(prefix = { "CONTAMINANT_DETECION_" }, flag = true, value = {
+ @IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = {
CONTAMINANT_DETECTION_NOT_SUPPORTED,
CONTAMINANT_DETECTION_DISABLED,
CONTAMINANT_DETECTION_NOT_DETECTED,
@@ -221,7 +221,7 @@ public final class UsbPortStatus implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@interface ContaminantProtectionStatus{}
- @IntDef(prefix = { "MODE_" }, flag = true, value = {
+ @IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
MODE_DFP,
MODE_UFP,
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 5576857d1f6b..6831eca32f72 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -630,6 +630,9 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public void updateInputMethodDisplay(int displayId) {
+ if (getDisplayId() == displayId) {
+ return;
+ }
// Update display for adding IME window to the right display.
// TODO(b/111364446) Need to address context lifecycle issue if need to re-create
// for update resources & configuration correctly when show soft input
@@ -804,12 +807,12 @@ public class InputMethodService extends AbstractInputMethodService {
null /* icProto */);
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
-
showWindow(true);
applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */);
+ } else {
+ // If user uses hard keyboard, IME button should always be shown.
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
- // If user uses hard keyboard, IME button should always be shown.
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
final boolean isVisible = isInputViewShown();
final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
@@ -1273,6 +1276,9 @@ public class InputMethodService extends AbstractInputMethodService {
super.onCreate();
mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
mSettingsObserver = SettingsObserver.createAndRegister(this);
+ // cache preference so we don't have to read ContentProvider when IME is requested to be
+ // shown the first time (cold start).
+ mSettingsObserver.shouldShowImeWithHardKeyboard();
mIsAutomotive = isAutomotive();
mAutomotiveHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
@@ -1341,13 +1347,7 @@ public class InputMethodService extends AbstractInputMethodService {
mRootView = mInflater.inflate(
com.android.internal.R.layout.input_method, null);
mWindow.setContentView(mRootView);
- mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
- if (Settings.Global.getInt(getContentResolver(),
- Settings.Global.FANCY_IME_ANIMATIONS, 0) != 0) {
- mWindow.getWindow().setWindowAnimations(
- com.android.internal.R.style.Animation_InputMethodFancy);
- }
mFullscreenArea = mRootView.findViewById(com.android.internal.R.id.fullscreenArea);
mExtractViewHidden = false;
mExtractFrame = mRootView.findViewById(android.R.id.extractArea);
@@ -1413,6 +1413,7 @@ public class InputMethodService extends AbstractInputMethodService {
int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
CompletionInfo[] completions = mCurCompletions;
+ mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsComputer);
initViews();
mInputViewStarted = false;
mCandidatesViewStarted = false;
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 522add12827e..8b06ebe105d0 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -19,7 +19,7 @@ package android.net;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.BitUtils;
+import java.util.BitSet;
/**
* Represents a core networking event defined in package android.net.metrics.
@@ -86,9 +86,7 @@ public final class ConnectivityMetricsEvent implements Parcelable {
if (ifname != null) {
buffer.append(", ").append(ifname);
}
- for (int t : BitUtils.unpackBits(transports)) {
- buffer.append(", ").append(NetworkCapabilities.transportNameOf(t));
- }
+ buffer.append(", transports=").append(BitSet.valueOf(new long[] { transports }));
buffer.append("): ").append(data.toString());
return buffer.toString();
}
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 9876fc650f21..183f500572bd 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -16,12 +16,9 @@
package android.net;
-import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
-import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
-import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
-
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.Preconditions.checkStringNotEmpty;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -158,9 +155,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
// IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
// networks, the VPN must provide a link fulfilling the stricter of the two conditions
// (at least that of the IPv6 MTU).
- if (mMaxMtu < LinkProperties.MIN_MTU_V6) {
- throw new IllegalArgumentException(
- "Max MTU must be at least" + LinkProperties.MIN_MTU_V6);
+ if (mMaxMtu < IPV6_MIN_MTU) {
+ throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU);
}
switch (mType) {
@@ -811,9 +807,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
// IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
// networks, the VPN must provide a link fulfilling the stricter of the two conditions
// (at least that of the IPv6 MTU).
- if (mtu < LinkProperties.MIN_MTU_V6) {
- throw new IllegalArgumentException(
- "Max MTU must be at least " + LinkProperties.MIN_MTU_V6);
+ if (mtu < IPV6_MIN_MTU) {
+ throw new IllegalArgumentException("Max MTU must be at least " + IPV6_MIN_MTU);
}
mMaxMtu = mtu;
return this;
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index fa1497dcbc43..b48c1fdaf1b2 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -17,8 +17,6 @@ package android.net;
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,7 +26,6 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -182,7 +179,6 @@ public final class IpSecTransform implements AutoCloseable {
try {
IIpSecService svc = getIpSecService();
svc.deleteTransform(mResourceId);
- stopNattKeepalive();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} catch (Exception e) {
@@ -213,36 +209,6 @@ public final class IpSecTransform implements AutoCloseable {
private int mResourceId;
private final Context mContext;
private final CloseGuard mCloseGuard = CloseGuard.get();
- private ConnectivityManager.PacketKeepalive mKeepalive;
- private Handler mCallbackHandler;
- private final ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
- new ConnectivityManager.PacketKeepaliveCallback() {
-
- @Override
- public void onStarted() {
- synchronized (this) {
- mCallbackHandler.post(() -> mUserKeepaliveCallback.onStarted());
- }
- }
-
- @Override
- public void onStopped() {
- synchronized (this) {
- mKeepalive = null;
- mCallbackHandler.post(() -> mUserKeepaliveCallback.onStopped());
- }
- }
-
- @Override
- public void onError(int error) {
- synchronized (this) {
- mKeepalive = null;
- mCallbackHandler.post(() -> mUserKeepaliveCallback.onError(error));
- }
- }
- };
-
- private NattKeepaliveCallback mUserKeepaliveCallback;
/** @hide */
@VisibleForTesting
@@ -274,76 +240,6 @@ public final class IpSecTransform implements AutoCloseable {
public void onError(int error) {}
}
- /**
- * Start a NAT-T keepalive session for the current transform.
- *
- * For a transform that is using UDP encapsulated IPv4, NAT-T offloading provides
- * a power efficient mechanism of sending NAT-T packets at a specified interval.
- *
- * @param userCallback a {@link #NattKeepaliveCallback} to receive asynchronous status
- * information about the requested NAT-T keepalive session.
- * @param intervalSeconds the interval between NAT-T keepalives being sent. The
- * the allowed range is between 20 and 3600 seconds.
- * @param handler a handler on which to post callbacks when received.
- *
- * @hide
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
- android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
- })
- public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
- int intervalSeconds, @NonNull Handler handler) throws IOException {
- checkNotNull(userCallback);
- if (intervalSeconds < 20 || intervalSeconds > 3600) {
- throw new IllegalArgumentException("Invalid NAT-T keepalive interval");
- }
- checkNotNull(handler);
- if (mResourceId == INVALID_RESOURCE_ID) {
- throw new IllegalStateException(
- "Packet keepalive cannot be started for an inactive transform");
- }
-
- synchronized (mKeepaliveCallback) {
- if (mKeepaliveCallback != null) {
- throw new IllegalStateException("Keepalive already active");
- }
-
- mUserKeepaliveCallback = userCallback;
- ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- mKeepalive = cm.startNattKeepalive(
- mConfig.getNetwork(), intervalSeconds, mKeepaliveCallback,
- NetworkUtils.numericToInetAddress(mConfig.getSourceAddress()),
- 4500, // FIXME urgently, we need to get the port number from the Encap socket
- NetworkUtils.numericToInetAddress(mConfig.getDestinationAddress()));
- mCallbackHandler = handler;
- }
- }
-
- /**
- * Stop an ongoing NAT-T keepalive session.
- *
- * Calling this API will request that an ongoing NAT-T keepalive session be terminated.
- * If this API is not called when a Transform is closed, the underlying NAT-T session will
- * be terminated automatically.
- *
- * @hide
- */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
- android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
- })
- public void stopNattKeepalive() {
- synchronized (mKeepaliveCallback) {
- if (mKeepalive == null) {
- Log.e(TAG, "No active keepalive to stop");
- return;
- }
- mKeepalive.stop();
- }
- }
-
/** This class is used to build {@link IpSecTransform} objects. */
public static class Builder {
private Context mContext;
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 81e6e788734b..e41ed72b259c 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -82,8 +82,7 @@ public final class LinkProperties implements Parcelable {
private static final int MIN_MTU = 68;
- /** @hide */
- public static final int MIN_MTU_V6 = 1280;
+ private static final int MIN_MTU_V6 = 1280;
private static final int MAX_MTU = 10000;
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 9c1038846d70..e65c27c2f4bb 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -17,8 +17,6 @@
package android.net;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.getNetworkTypeName;
-import static android.net.ConnectivityManager.isNetworkTypeMobile;
import android.annotation.Nullable;
import android.content.Context;
@@ -27,7 +25,6 @@ import android.net.wifi.WifiManager;
import android.os.Build;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import java.util.Objects;
@@ -85,7 +82,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("{");
- builder.append("type=").append(getNetworkTypeName(mType));
+ builder.append("type=").append(mType);
builder.append(", subType=");
if (mSubType == SUBTYPE_COMBINED) {
builder.append("COMBINED");
@@ -195,18 +192,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
boolean metered = !state.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- if (isNetworkTypeMobile(type)) {
- if (state.subscriberId == null) {
- if (state.networkInfo.getState() != NetworkInfo.State.DISCONNECTED &&
- state.networkInfo.getState() != NetworkInfo.State.UNKNOWN) {
- Slog.w(TAG, "Active mobile network without subscriber! ni = "
- + state.networkInfo);
- }
- }
-
- subscriberId = state.subscriberId;
+ subscriberId = state.subscriberId;
- } else if (type == TYPE_WIFI) {
+ if (type == TYPE_WIFI) {
if (state.networkId != null) {
networkId = state.networkId;
} else {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index a643d09eef49..f05f033b2fa5 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -144,6 +144,8 @@ public class NetworkPolicyManager {
public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby";
/** @hide */
public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave";
+ /** @hide */
+ public static final String FIREWALL_CHAIN_NAME_RESTRICTED = "restricted";
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index a17a49897d39..14cb51c85d06 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -63,7 +63,7 @@ public class NetworkProvider {
private final Messenger mMessenger;
private final String mName;
- private final ConnectivityManager mCm;
+ private final Context mContext;
private int mProviderId = ID_NONE;
@@ -78,8 +78,6 @@ public class NetworkProvider {
*/
@SystemApi
public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) {
- mCm = ConnectivityManager.from(context);
-
Handler handler = new Handler(looper) {
@Override
public void handleMessage(Message m) {
@@ -95,6 +93,7 @@ public class NetworkProvider {
}
}
};
+ mContext = context;
mMessenger = new Messenger(handler);
mName = name;
}
@@ -158,6 +157,6 @@ public class NetworkProvider {
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
- mCm.declareNetworkRequestUnfulfillable(request);
+ ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request);
}
}
diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java
index b320b755f3f4..c67259d6b29b 100644
--- a/core/java/android/net/metrics/ConnectStats.java
+++ b/core/java/android/net/metrics/ConnectStats.java
@@ -16,14 +16,14 @@
package android.net.metrics;
-import android.net.NetworkCapabilities;
import android.system.OsConstants;
import android.util.IntArray;
import android.util.SparseIntArray;
-import com.android.internal.util.BitUtils;
import com.android.internal.util.TokenBucket;
+import java.util.BitSet;
+
/**
* A class that aggregates connect() statistics.
* {@hide}
@@ -120,10 +120,9 @@ public class ConnectStats {
@Override
public String toString() {
StringBuilder builder =
- new StringBuilder("ConnectStats(").append("netId=").append(netId).append(", ");
- for (int t : BitUtils.unpackBits(transports)) {
- builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
- }
+ new StringBuilder("ConnectStats(").append("netId=").append(netId)
+ .append(", transports=").append(BitSet.valueOf(new long[] { transports }))
+ .append(", ");
builder.append(String.format("%d events, ", eventCount));
builder.append(String.format("%d success, ", connectCount));
builder.append(String.format("%d blocking, ", connectBlockingCount));
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index 6f383b4d515b..8988983d175c 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -16,12 +16,7 @@
package android.net.metrics;
-import static android.net.ConnectivityManager.NETID_UNSET;
-
-import android.net.NetworkCapabilities;
-
-import com.android.internal.util.BitUtils;
-
+import java.util.BitSet;
import java.util.StringJoiner;
/**
@@ -32,8 +27,8 @@ public class DefaultNetworkEvent {
// The creation time in milliseconds of this DefaultNetworkEvent.
public final long creationTimeMs;
- // The network ID of the network or NETID_UNSET if none.
- public int netId = NETID_UNSET;
+ // The network ID of the network or 0 if none.
+ public int netId = 0;
// The list of transport types, as defined in NetworkCapabilities.java.
public int transports;
// The list of transport types of the last previous default network.
@@ -63,9 +58,7 @@ public class DefaultNetworkEvent {
public String toString() {
StringJoiner j = new StringJoiner(", ", "DefaultNetworkEvent(", ")");
j.add("netId=" + netId);
- for (int t : BitUtils.unpackBits(transports)) {
- j.add(NetworkCapabilities.transportNameOf(t));
- }
+ j.add("transports=" + BitSet.valueOf(new long[] { transports }));
j.add("ip=" + ipSupport());
if (initialScore > 0) {
j.add("initial_score=" + initialScore);
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index 5aa705b0edf3..bf351ce07fe8 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -16,11 +16,8 @@
package android.net.metrics;
-import android.net.NetworkCapabilities;
-
-import com.android.internal.util.BitUtils;
-
import java.util.Arrays;
+import java.util.BitSet;
/**
* A batch of DNS events recorded by NetdEventListenerService for a specific network.
@@ -86,10 +83,10 @@ final public class DnsEvent {
@Override
public String toString() {
StringBuilder builder =
- new StringBuilder("DnsEvent(").append("netId=").append(netId).append(", ");
- for (int t : BitUtils.unpackBits(transports)) {
- builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
- }
+ new StringBuilder("DnsEvent(").append("netId=").append(netId)
+ .append(", transports=")
+ .append(BitSet.valueOf(new long[] { transports }))
+ .append(", ");
builder.append(String.format("%d events, ", eventCount));
builder.append(String.format("%d success)", successCount));
return builder.toString();
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 66d92c48087c..8f2f612cf53b 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -16,11 +16,9 @@
package android.net.metrics;
-import android.net.NetworkCapabilities;
-
-import com.android.internal.util.BitUtils;
import com.android.internal.util.TokenBucket;
+import java.util.BitSet;
import java.util.StringJoiner;
/**
@@ -144,9 +142,7 @@ public class NetworkMetrics {
public String toString() {
StringJoiner j = new StringJoiner(", ", "{", "}");
j.add("netId=" + netId);
- for (int t : BitUtils.unpackBits(transports)) {
- j.add(NetworkCapabilities.transportNameOf(t));
- }
+ j.add("transports=" + BitSet.valueOf(new long[] { transports }));
j.add(String.format("dns avg=%dms max=%dms err=%.1f%% tot=%d",
(int) dnsLatencies.average(), (int) dnsLatencies.max,
100 * dnsErrorRate.average(), dnsErrorRate.count));
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
new file mode 100644
index 000000000000..d00c3c361722
--- /dev/null
+++ b/core/java/android/os/BatteryConsumer.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for objects containing battery attribution data.
+ *
+ * @hide
+ */
+public abstract class BatteryConsumer {
+
+ /**
+ * Power usage component, describing the particular part of the system
+ * responsible for power drain.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"POWER_COMPONENT_"}, value = {
+ POWER_COMPONENT_CPU,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public static @interface PowerComponent {
+ }
+
+ public static final int POWER_COMPONENT_CPU = 0;
+
+ public static final int POWER_COMPONENT_COUNT = 1;
+
+ public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
+ public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
+
+ private final PowerComponents mPowerComponents;
+
+ protected BatteryConsumer(@NonNull PowerComponents powerComponents) {
+ mPowerComponents = powerComponents;
+ }
+
+ /**
+ * Total power consumed by this consumer, in mAh.
+ */
+ public double getConsumedPower() {
+ return mPowerComponents.getTotalPowerConsumed();
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPower(@PowerComponent int componentId) {
+ return mPowerComponents.getConsumedPower(componentId);
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPowerForCustomComponent(int componentId) {
+ return mPowerComponents.getConsumedPowerForCustomComponent(componentId);
+ }
+
+ protected void writeToParcel(Parcel dest, int flags) {
+ mPowerComponents.writeToParcel(dest, flags);
+ }
+}
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index a9585c62866b..d9e01cd4fc7d 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -161,6 +161,22 @@ public final class BatteryStatsManager {
}
/**
+ * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+ * and per-UID basis.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.BATTERY_STATS)
+ @NonNull
+ public BatteryUsageStats getBatteryUsageStats() {
+ try {
+ return mBatteryStats.getBatteryUsageStats();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Indicates that the wifi connection RSSI has changed.
*
* @param newRssi The new RSSI value.
diff --git a/cmds/statsd/benchmark/main.cpp b/core/java/android/os/BatteryUsageStats.aidl
index 08921f3c3f52..0400f19974f1 100644
--- a/cmds/statsd/benchmark/main.cpp
+++ b/core/java/android/os/BatteryUsageStats.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-#include <benchmark/benchmark.h>
+package android.os;
-BENCHMARK_MAIN();
+parcelable BatteryUsageStats;
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
new file mode 100644
index 000000000000..3f036cdcfa72
--- /dev/null
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis.
+ *
+ * @hide
+ */
+public final class BatteryUsageStats implements Parcelable {
+ private final double mConsumedPower;
+ private final int mDischargePercentage;
+ private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers;
+
+ private BatteryUsageStats(@NonNull Builder builder) {
+ mConsumedPower = builder.mConsumedPower;
+ mDischargePercentage = builder.mDischargePercentage;
+ mUidBatteryConsumers = builder.mUidBatteryConsumers;
+ }
+
+ /**
+ * Portion of battery charge drained since BatteryStats reset (e.g. due to being fully
+ * charged), as percentage of the full charge in the range [0:100]
+ */
+ public int getDischargePercentage() {
+ return mDischargePercentage;
+ }
+
+ /**
+ * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
+ * charged), in mAh
+ */
+ public double getConsumedPower() {
+ return mConsumedPower;
+ }
+
+ @NonNull
+ public List<UidBatteryConsumer> getUidBatteryConsumers() {
+ return mUidBatteryConsumers;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private BatteryUsageStats(@NonNull Parcel source) {
+ mUidBatteryConsumers = new ArrayList<>();
+ source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader());
+ mConsumedPower = source.readDouble();
+ mDischargePercentage = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelableList(mUidBatteryConsumers, flags);
+ dest.writeDouble(mConsumedPower);
+ dest.writeInt(mDischargePercentage);
+ }
+
+ @NonNull
+ public static final Creator<BatteryUsageStats> CREATOR = new Creator<BatteryUsageStats>() {
+ public BatteryUsageStats createFromParcel(@NonNull Parcel source) {
+ return new BatteryUsageStats(source);
+ }
+
+ public BatteryUsageStats[] newArray(int size) {
+ return new BatteryUsageStats[size];
+ }
+ };
+
+ /**
+ * Builder for BatteryUsageStats.
+ */
+ public static final class Builder {
+ private double mConsumedPower;
+ private int mDischargePercentage;
+ private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers = new ArrayList<>();
+
+ /**
+ * Constructs a read-only object using the Builder values.
+ */
+ @NonNull
+ public BatteryUsageStats build() {
+ return new BatteryUsageStats(this);
+ }
+
+ /**
+ * Sets the battery discharge amount since BatteryStats reset as percentage of the full
+ * charge.
+ */
+ @SuppressLint("PercentageInt") // See b/174188159
+ @NonNull
+ public Builder setDischargePercentage(int dischargePercentage) {
+ mDischargePercentage = dischargePercentage;
+ return this;
+ }
+
+ /**
+ * Sets the battery discharge amount since BatteryStats reset, in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(double consumedPower) {
+ mConsumedPower = consumedPower;
+ return this;
+ }
+
+ /**
+ * Adds a UidBatteryConsumer, which represents battery attribution data for an
+ * individual UID.
+ */
+ @NonNull
+ public Builder addUidBatteryConsumer(@NonNull UidBatteryConsumer uidBatteryConsumer) {
+ mUidBatteryConsumers.add(uidBatteryConsumer);
+ return this;
+ }
+ }
+}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index a4af0dbed0bd..e2e1bbe8487f 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1946,7 +1946,13 @@ public final class Debug
*/
public static final int MEMINFO_KRECLAIMABLE = 15;
/** @hide */
- public static final int MEMINFO_COUNT = 16;
+ public static final int MEMINFO_ACTIVE = 16;
+ /** @hide */
+ public static final int MEMINFO_INACTIVE = 17;
+ /** @hide */
+ public static final int MEMINFO_UNEVICTABLE = 18;
+ /** @hide */
+ public static final int MEMINFO_COUNT = 19;
/**
* Retrieves /proc/meminfo. outSizes is filled with fields
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 5db4107f02f1..379d6e6f5dfe 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1436,7 +1436,8 @@ public final class FileUtils {
public static FileDescriptor convertToModernFd(FileDescriptor fd) {
try {
Context context = AppGlobals.getInitialApplication();
- if (!SystemProperties.getBoolean("persist.sys.fuse.transcode", false)
+ // TODO(b/169327180): Consider device config.
+ if (!SystemProperties.getBoolean("persist.sys.fuse.transcode_enabled", false)
|| !SystemProperties.getBoolean("persist.sys.fuse.transcode_optimize", true)
|| UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
// If transcode is enabled we optimize by default, unless explicitly disabled.
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
new file mode 100644
index 000000000000..42ba1ff60e5a
--- /dev/null
+++ b/core/java/android/os/PowerComponents.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+import android.annotation.NonNull;
+
+/**
+ * Contains details of battery attribution data broken down to individual power drain types
+ * such as CPU, RAM, GPU etc.
+ *
+ * @hide
+ */
+class PowerComponents {
+
+ private final double mTotalPowerConsumed;
+ private final double[] mPowerComponents;
+
+ PowerComponents(@NonNull Builder builder) {
+ mTotalPowerConsumed = builder.mTotalPowerConsumed;
+ mPowerComponents = builder.mPowerComponents;
+ }
+
+ PowerComponents(@NonNull Parcel source) {
+ mTotalPowerConsumed = source.readDouble();
+ mPowerComponents = source.createDoubleArray();
+ }
+
+ /** Writes contents to Parcel */
+ void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mTotalPowerConsumed);
+ dest.writeDoubleArray(mPowerComponents);
+ }
+
+ /**
+ * Total power consumed by this consumer, in mAh.
+ */
+ public double getTotalPowerConsumed() {
+ return mTotalPowerConsumed;
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPower(@UidBatteryConsumer.PowerComponent int componentId) {
+ if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) {
+ throw new IllegalArgumentException(
+ "Unsupported power component ID: " + componentId);
+ }
+ try {
+ return mPowerComponents[componentId];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Unsupported power component ID: " + componentId);
+ }
+ }
+
+ /**
+ * Returns the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @return Amount of consumed power in mAh.
+ */
+ public double getConsumedPowerForCustomComponent(int componentId) {
+ if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ try {
+ return mPowerComponents[
+ BatteryConsumer.POWER_COMPONENT_COUNT + componentId
+ - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ }
+
+ /**
+ * Builder for PowerComponents.
+ */
+ static final class Builder {
+ private double mTotalPowerConsumed;
+ private final double[] mPowerComponents;
+
+ Builder(int customPowerComponentCount) {
+ mPowerComponents = new double[BatteryConsumer.POWER_COMPONENT_COUNT
+ + customPowerComponentCount];
+ }
+
+ /**
+ * Sets the sum amount of power consumed since BatteryStats reset.
+ */
+ @NonNull
+ public Builder setTotalPowerConsumed(double totalPowerConsumed) {
+ mTotalPowerConsumed = totalPowerConsumed;
+ return this;
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(@UidBatteryConsumer.PowerComponent int componentId,
+ double componentPower) {
+ if (componentId >= BatteryConsumer.POWER_COMPONENT_COUNT) {
+ throw new IllegalArgumentException(
+ "Unsupported power component ID: " + componentId);
+ }
+ try {
+ mPowerComponents[componentId] = componentPower;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(
+ "Unsupported power component ID: " + componentId);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
+ if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ try {
+ mPowerComponents[BatteryConsumer.POWER_COMPONENT_COUNT + componentId
+ - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID] = componentPower;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(
+ "Unsupported custom power component ID: " + componentId);
+ }
+ return this;
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public PowerComponents build() {
+ return new PowerComponents(this);
+ }
+ }
+}
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
new file mode 100644
index 000000000000..7dcbf7d4cef3
--- /dev/null
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Contains power consumption data attributed to a specific UID.
+ *
+ * @hide
+ */
+public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable {
+
+ private final int mUid;
+ @Nullable
+ private final String mPackageWithHighestDrain;
+
+ public int getUid() {
+ return mUid;
+ }
+
+ @Nullable
+ public String getPackageWithHighestDrain() {
+ return mPackageWithHighestDrain;
+ }
+
+ private UidBatteryConsumer(@NonNull Builder builder) {
+ super(builder.mPowerComponentsBuilder.build());
+ mUid = builder.mUid;
+ mPackageWithHighestDrain = builder.mPackageWithHighestDrain;
+ }
+
+ private UidBatteryConsumer(@NonNull Parcel source) {
+ super(new PowerComponents(source));
+ mUid = source.readInt();
+ mPackageWithHighestDrain = source.readString();
+ }
+
+ /**
+ * Writes the contents into a Parcel.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mUid);
+ dest.writeString(mPackageWithHighestDrain);
+ }
+
+ @NonNull
+ public static final Creator<UidBatteryConsumer> CREATOR = new Creator<UidBatteryConsumer>() {
+ public UidBatteryConsumer createFromParcel(@NonNull Parcel source) {
+ return new UidBatteryConsumer(source);
+ }
+
+ public UidBatteryConsumer[] newArray(int size) {
+ return new UidBatteryConsumer[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Builder for UidBatteryConsumer.
+ */
+ public static final class Builder {
+ private final PowerComponents.Builder mPowerComponentsBuilder;
+ private final int mUid;
+ private String mPackageWithHighestDrain;
+
+ public Builder(int customPowerComponentCount, int uid) {
+ mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount);
+ mUid = uid;
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public UidBatteryConsumer build() {
+ return new UidBatteryConsumer(this);
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
+ *
+ * @param componentId The ID of the power component, e.g.
+ * {@link BatteryConsumer#POWER_COMPONENT_CPU}.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(@PowerComponent int componentId, double componentPower) {
+ mPowerComponentsBuilder.setConsumedPower(componentId, componentPower);
+ return this;
+ }
+
+ /**
+ * Sets the amount of drain attributed to the specified custom drain type.
+ *
+ * @param componentId The ID of the custom power component.
+ * @param componentPower Amount of consumed power in mAh.
+ */
+ @NonNull
+ public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
+ mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower);
+ return this;
+ }
+
+ /**
+ * Sets the amount of power consumed since BatteryStats reset, mAh.
+ */
+ @NonNull
+ public Builder setConsumedPower(double consumedPower) {
+ mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower);
+ return this;
+ }
+
+ /**
+ * Sets the name of the package owned by this UID that consumed the highest amount
+ * of power since BatteryStats reset.
+ */
+ @NonNull
+ public Builder setPackageWithHighestDrain(@Nullable String packageName) {
+ mPackageWithHighestDrain = packageName;
+ return this;
+ }
+ }
+}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 100166814975..a94077dd7ad6 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -73,10 +73,6 @@ interface IPermissionManager {
void resetRuntimePermissions();
- boolean setDefaultBrowser(String packageName, int userId);
-
- String getDefaultBrowser(int userId);
-
void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId);
void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId);
@@ -91,10 +87,6 @@ interface IPermissionManager {
void revokeDefaultPermissionsFromLuiApps(in String[] packageNames, int userId);
- void setPermissionEnforced(String permName, boolean enforced);
-
- boolean isPermissionEnforced(String permName);
-
boolean shouldShowRequestPermissionRationale(String permName,
String packageName, int userId);
diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java
index 3134ec06fe50..a4676c47bac3 100644
--- a/core/java/android/permission/PermissionManagerInternal.java
+++ b/core/java/android/permission/PermissionManagerInternal.java
@@ -19,11 +19,6 @@ package android.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.os.UserHandle;
-
-import com.android.internal.util.function.TriFunction;
-
-import java.util.function.BiFunction;
/**
* Internal interfaces to be used by other components within the system server.
@@ -32,96 +27,39 @@ import java.util.function.BiFunction;
*
* @hide
*/
-public abstract class PermissionManagerInternal {
-
- /**
- * Listener for package permission state (permissions or flags) changes.
- */
- public interface OnRuntimePermissionStateChangedListener {
-
- /**
- * Called when the runtime permission state (permissions or flags) changed.
- *
- * @param packageName The package for which the change happened.
- * @param userId the user id for which the change happened.
- */
- @Nullable
- void onRuntimePermissionStateChanged(@NonNull String packageName,
- @UserIdInt int userId);
- }
-
- /** Interface to override permission checks via composition */
- public interface CheckPermissionDelegate {
- /**
- * Checks whether the given package has been granted the specified permission.
- *
- * @return If the package has the permission, PERMISSION_GRANTED is
- * returned. If it does not have the permission, PERMISSION_DENIED
- * is returned.
- *
- * @see android.content.pm.PackageManager#checkPermission(String, String)
- */
- int checkPermission(String permName, String pkgName, int userId,
- TriFunction<String, String, Integer, Integer> superImpl);
-
- /**
- /**
- * Checks whether the given uid has been granted the specified permission.
- *
- * @return If the package has the permission, PERMISSION_GRANTED is
- * returned. If it does not have the permission, PERMISSION_DENIED
- * is returned.
- *
- */
- int checkUidPermission(String permName, int uid,
- BiFunction<String, Integer, Integer> superImpl);
- }
-
+public interface PermissionManagerInternal {
/**
- * Get the state of the runtime permissions as xml file.
+ * Get the state of the runtime permissions as a blob.
*
- * @param user The user the data should be extracted for
+ * @param userId The user ID the data should be extracted for
*
- * @return The state as a xml file
+ * @return the state as a blob
*/
- public abstract @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user);
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ @Nullable
+ byte[] backupRuntimePermissions(@UserIdInt int userId);
/**
* Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
+ * <p>
+ * If not all state can be restored, the un-restorable state will be delayed and can be
+ * retried via {@link #restoreDelayedRuntimePermissions}.
*
- * <p>If not all state can be restored, the un-restoreable state will be delayed and can be
- * re-tried via {@link #restoreDelayedRuntimePermissions}.
- *
- * @param backup The state as an xml file
- * @param user The user the data should be restored for
+ * @param backup the state as a blob
+ * @param userId the user ID the data should be restored for
*/
- public abstract void restoreRuntimePermissions(@NonNull byte[] backup,
- @NonNull UserHandle user);
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId);
/**
* Try to apply permission backup of a package that was previously not applied.
*
- * @param packageName The package that is newly installed
- * @param user The user the package is installed for
+ * @param packageName the package that is newly installed
+ * @param userId the user ID the package is installed for
*
* @see #restoreRuntimePermissions
*/
- public abstract void restoreDelayedRuntimePermissions(@NonNull String packageName,
- @NonNull UserHandle user);
-
- /**
- * Adds a listener for runtime permission state (permissions or flags) changes.
- *
- * @param listener The listener.
- */
- public abstract void addOnRuntimePermissionStateChangedListener(
- @NonNull OnRuntimePermissionStateChangedListener listener);
-
- /**
- * Removes a listener for runtime permission state (permissions or flags) changes.
- *
- * @param listener The listener.
- */
- public abstract void removeOnRuntimePermissionStateChangedListener(
- @NonNull OnRuntimePermissionStateChangedListener listener);
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ void restoreDelayedRuntimePermissions(@NonNull String packageName,
+ @UserIdInt int userId);
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 03e8a070b237..da06e821ea9f 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -19,6 +19,7 @@ package android.provider;
import android.accounts.Account;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -47,6 +48,9 @@ import android.database.DatabaseUtils;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
@@ -54,11 +58,15 @@ import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.View;
+import com.google.android.collect.Sets;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
/**
* <p>
@@ -8188,6 +8196,321 @@ public final class ContactsContract {
public static final String RAW_CONTACT_ID2 = "raw_contact_id2";
}
+
+ /**
+ * Class containing utility methods around determine what accounts in the ContactsProvider are
+ * related to the SIM cards in the device.
+ * <p>
+ * Apps interested in managing contacts from SIM cards can query the ContactsProvider using
+ * {@link #getSimAccounts(ContentResolver)} to get all accounts that relate to SIM cards. They
+ * can also register a receiver for the {@link #ACTION_SIM_ACCOUNTS_CHANGED} broadcast to be
+ * notified when these accounts change.
+ */
+ public static final class SimContacts {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private SimContacts() {
+ }
+
+ /**
+ * The method to invoke in order to add a new SIM account for a newly inserted SIM card.
+ *
+ * @hide
+ */
+ public static final String ADD_SIM_ACCOUNT_METHOD = "addSimAccount";
+
+ /**
+ * The method to invoke in order to remove a SIM account once the corresponding SIM card is
+ * ejected.
+ *
+ * @hide
+ */
+ public static final String REMOVE_SIM_ACCOUNT_METHOD = "removeSimAccount";
+
+ /**
+ * The method to invoke in order to query all SIM accounts.
+ *
+ * @hide
+ */
+ public static final String QUERY_SIM_ACCOUNTS_METHOD = "querySimAccounts";
+
+ /**
+ * Key to add in the outgoing Bundle for the SIM slot.
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_SLOT_INDEX = "key_sim_slot_index";
+
+ /**
+ * Key to add in the outgoing Bundle for the SIM account's EF type.
+ * See {@link SimAccount#mEfType} for more information.
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_EF_TYPE = "key_sim_ef_type";
+
+ /**
+ * Key to add in the outgoing Bundle for the account name.
+ *
+ * @hide
+ */
+ public static final String KEY_ACCOUNT_NAME = "key_sim_account_name";
+
+ /**
+ * Key to add in the outgoing Bundle for the account type.
+ *
+ * @hide
+ */
+ public static final String KEY_ACCOUNT_TYPE = "key_sim_account_type";
+
+ /**
+ * Key in the incoming Bundle for the all the SIM accounts.
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_ACCOUNTS = "key_sim_accounts";
+
+ /**
+ * Broadcast Action: SIM accounts have changed, call
+ * {@link #getSimAccounts(ContentResolver)} to get the latest.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SIM_ACCOUNTS_CHANGED =
+ "android.provider.action.SIM_ACCOUNTS_CHANGED";
+
+ /**
+ * Adds a new SIM account that maps to the corresponding SIM slot.
+ *
+ * @param accountName accountName value for the account
+ * @param accountType accountType value for the account
+ * @param contentResolver to perform the operation on.
+ * @param simSlotIndex the SIM slot index of this new account.
+ * @param efType the EF type of this new account.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS")
+ public static void addSimAccount(@NonNull ContentResolver contentResolver,
+ @NonNull String accountName,
+ @NonNull String accountType,
+ int simSlotIndex,
+ int efType) {
+ if (simSlotIndex < 0) {
+ throw new IllegalArgumentException("Sim slot is negative");
+ }
+ if (!SimAccount.getValidEfTypes().contains(efType)) {
+ throw new IllegalArgumentException("Invalid EF type");
+ }
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ throw new IllegalArgumentException("Account name or type is empty");
+ }
+
+ Bundle extras = new Bundle();
+ extras.putInt(KEY_SIM_SLOT_INDEX, simSlotIndex);
+ extras.putInt(KEY_SIM_EF_TYPE, efType);
+ extras.putString(KEY_ACCOUNT_NAME, accountName);
+ extras.putString(KEY_ACCOUNT_TYPE, accountType);
+
+ contentResolver.call(ContactsContract.AUTHORITY_URI,
+ ContactsContract.SimContacts.ADD_SIM_ACCOUNT_METHOD,
+ null, extras);
+ }
+
+ /**
+ * Removes all SIM accounts that map to the corresponding SIM slot.
+ *
+ * @param contentResolver to perform the operation on.
+ * @param simSlotIndex the SIM slot index of the accounts to remove.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission("android.contacts.permission.MANAGE_SIM_ACCOUNTS")
+ public static void removeSimAccounts(@NonNull ContentResolver contentResolver,
+ int simSlotIndex) {
+ if (simSlotIndex < 0) {
+ throw new IllegalArgumentException("Sim slot is negative");
+ }
+
+ Bundle extras = new Bundle();
+ extras.putInt(KEY_SIM_SLOT_INDEX, simSlotIndex);
+
+ contentResolver.call(ContactsContract.AUTHORITY_URI,
+ ContactsContract.SimContacts.REMOVE_SIM_ACCOUNT_METHOD,
+ null, extras);
+ }
+
+ /**
+ * Returns all known SIM accounts. May be empty but never null.
+ *
+ * @param contentResolver content resolver to query.
+ */
+ public static @NonNull List<SimAccount> getSimAccounts(
+ @NonNull ContentResolver contentResolver) {
+ Bundle response = contentResolver.call(ContactsContract.AUTHORITY_URI,
+ ContactsContract.SimContacts.QUERY_SIM_ACCOUNTS_METHOD,
+ null, null);
+ List<SimAccount> result = response.getParcelableArrayList(KEY_SIM_ACCOUNTS);
+
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+
+ return result;
+ }
+ }
+
+ /**
+ * A parcelable class encapsulating account data for contacts that originate from a SIM card.
+ */
+ public static final class SimAccount implements Parcelable {
+ /** An invalid EF type identifier. */
+ public static final int UNKNOWN_EF_TYPE = 0;
+ /** EF type identifier for the ADN partition. */
+ public static final int ADN_EF_TYPE = 1;
+ /** EF type identifier for the SDN partition. */
+ public static final int SDN_EF_TYPE = 2;
+ /** EF type identifier for the FDN partition. */
+ public static final int FDN_EF_TYPE = 3;
+
+ /**
+ * The account_name of this SIM account. See {@link RawContacts#ACCOUNT_NAME}.
+ */
+ private final String mAccountName;
+
+ /**
+ * The account_type of this SIM account. See {@link RawContacts#ACCOUNT_TYPE}.
+ */
+ private final String mAccountType;
+
+ /**
+ * The slot index of the SIM card this account maps to. See {@link
+ * android.telephony.SubscriptionInfo#getSimSlotIndex()}.
+ */
+ private final int mSimSlotIndex;
+
+ /**
+ * The EF type of the contacts stored in this account. One of
+ * {@link #ADN_EF_TYPE}, {@link #SDN_EF_TYPE} or {@link #FDN_EF_TYPE}.
+ *
+ * EF type is the Elementary File type of the partition these contacts come from within the
+ * SIM card.
+ *
+ * ADN is the "abbreviated dialing numbers" or the user managed SIM contacts.
+ *
+ * SDN is the "service dialing numbers" which are usually preloaded onto the SIM by the
+ * carrier.
+ *
+ * FDN is the "fixed dialing numbers" which are contacts which can only be dialed from that
+ * SIM, used in cases such as parental control.
+ */
+ private final int mEfType;
+
+ /**
+ * @return A set containing all known EF type values
+ * @hide
+ */
+ public static @NonNull Set<Integer> getValidEfTypes() {
+ return Sets.newArraySet(ADN_EF_TYPE, SDN_EF_TYPE, FDN_EF_TYPE);
+ }
+
+ /**
+ * @hide
+ */
+ public SimAccount(@NonNull String accountName, @NonNull String accountType,
+ int simSlotIndex,
+ int efType) {
+ this.mAccountName = accountName;
+ this.mAccountType = accountType;
+ this.mSimSlotIndex = simSlotIndex;
+ this.mEfType = efType;
+ }
+
+ /**
+ * @return The account_name of this SIM account. See {@link RawContacts#ACCOUNT_NAME}.
+ */
+ public @NonNull String getAccountName() {
+ return mAccountName;
+ }
+
+ /**
+ * @return The account_type of this SIM account. See {@link RawContacts#ACCOUNT_TYPE}.
+ */
+ public @NonNull String getAccountType() {
+ return mAccountType;
+ }
+
+ /**
+ * @return The slot index of the SIM card this account maps to. See
+ * {@link android.telephony.SubscriptionInfo#getSimSlotIndex()}.
+ */
+ public int getSimSlotIndex() {
+ return mSimSlotIndex;
+ }
+
+ /**
+ * @return The EF type of the contacts stored in this account.
+ */
+ public int getEfType() {
+ return mEfType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAccountName, mAccountType, mSimSlotIndex, mEfType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (obj == this) return true;
+
+ SimAccount toCompare;
+ try {
+ toCompare = (SimAccount) obj;
+ } catch (ClassCastException ex) {
+ return false;
+ }
+
+ return mSimSlotIndex == toCompare.mSimSlotIndex
+ && mEfType == toCompare.mEfType
+ && Objects.equals(mAccountName, toCompare.mAccountName)
+ && Objects.equals(mAccountType, toCompare.mAccountType);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mAccountName);
+ dest.writeString(mAccountType);
+ dest.writeInt(mSimSlotIndex);
+ dest.writeInt(mEfType);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<SimAccount> CREATOR =
+ new Parcelable.Creator<SimAccount>() {
+ @Override
+ public SimAccount createFromParcel(Parcel source) {
+ String accountName = source.readString();
+ String accountType = source.readString();
+ int simSlot = source.readInt();
+ int efType = source.readInt();
+ SimAccount simAccount = new SimAccount(accountName, accountType, simSlot,
+ efType);
+ return simAccount;
+ }
+
+ @Override
+ public SimAccount[] newArray(int size) {
+ return new SimAccount[size];
+ }
+ };
+ }
+
/**
* @see Settings
*/
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 44cc0f5c330d..f58fa1595f4a 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -126,6 +126,14 @@ public final class DeviceConfig {
public static final String NAMESPACE_AUTOFILL = "autofill";
/**
+ * Namespace for battery saver feature.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_BATTERY_SAVER = "battery_saver";
+
+ /**
* Namespace for blobstore feature that allows apps to share data blobs.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 249a781509ac..12791bcc194b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9797,12 +9797,13 @@ public final class Settings {
"use_blast_adapter_sv";
/**
- * If {@code true}, vendor provided window manager display settings will be ignored.
- * (0 = false, 1 = true)
+ * Path to the WindowManager display settings file. If unset, the default file path will
+ * be used.
+ *
* @hide
*/
- public static final String DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS =
- "ignore_vendor_display_settings";
+ public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH =
+ "wm_display_settings_path";
/**
* Whether user has enabled development settings.
@@ -9961,13 +9962,13 @@ public final class Settings {
* upon going to sleep. It additionally controls whether a playback device attempts to turn
* on the connected Audio system when waking up. Supported values are:
* <ul>
- * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_TO_TV} Upon going to sleep, device
+ * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV} Upon going to sleep, device
* sends {@code <Standby>} to TV only. Upon waking up, device does not turn on the Audio
* system via {@code <System Audio Mode Request>}.</li>
- * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_BROADCAST} Upon going to sleep,
+ * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_BROADCAST} Upon going to sleep,
* device sends {@code <Standby>} to all devices in the network. Upon waking up, device
* attempts to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
- * <li>{@link HdmiControlManager#SEND_STANDBY_ON_SLEEP_NONE} Upon going to sleep, device
+ * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_NONE} Upon going to sleep, device
* does not send any {@code <Standby>} message. Upon waking up, device does not turn on the
* Audio system via {@code <System Audio Mode Request>}.</li>
* </ul>
@@ -11716,24 +11717,6 @@ public final class Settings {
"battery_saver_device_specific_constants";
/**
- * Settings for adaptive Battery Saver mode. Uses the same flags as
- * {@link #BATTERY_SAVER_CONSTANTS}.
- *
- * @hide
- */
- public static final String BATTERY_SAVER_ADAPTIVE_CONSTANTS =
- "battery_saver_adaptive_constants";
-
- /**
- * Device specific settings for adaptive Battery Saver mode. Uses the same flags as
- * {@link #BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS}.
- *
- * @hide
- */
- public static final String BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS =
- "battery_saver_adaptive_device_specific_constants";
-
- /**
* Battery tip specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java
index 88a78c36d112..61213e6293ba 100644
--- a/core/java/android/service/carrier/CarrierMessagingService.java
+++ b/core/java/android/service/carrier/CarrierMessagingService.java
@@ -16,6 +16,7 @@
package android.service.carrier;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -25,6 +26,8 @@ import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -74,6 +77,15 @@ public abstract class CarrierMessagingService extends Service {
*/
public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE = 0x2;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "RECEIVE_OPTIONS_" }, value = {
+ RECEIVE_OPTIONS_DEFAULT,
+ RECEIVE_OPTIONS_DROP,
+ RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FilterCompleteResult{}
+
/**
* Indicates that an SMS or MMS message was successfully sent.
*/
@@ -89,6 +101,15 @@ public abstract class CarrierMessagingService extends Service {
*/
public static final int SEND_STATUS_ERROR = 2;
+ /** @hide */
+ @IntDef(prefix = { "SEND_STATUS_" }, value = {
+ SEND_STATUS_OK,
+ SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
+ SEND_STATUS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SendResult {}
+
/**
* Successfully downloaded an MMS message.
*/
@@ -104,10 +125,26 @@ public abstract class CarrierMessagingService extends Service {
*/
public static final int DOWNLOAD_STATUS_ERROR = 2;
+ /** @hide */
+ @IntDef(prefix = { "DOWNLOAD_STATUS_" }, value = {
+ DOWNLOAD_STATUS_OK,
+ DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK,
+ DOWNLOAD_STATUS_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DownloadResult {}
+
/**
* Flag to request SMS delivery status report.
*/
- public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1;
+ public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 0x1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SEND_FLAG_" }, value = {
+ SEND_FLAG_REQUEST_DELIVERY_STATUS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SendRequest {}
private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper();
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index 4ffffc6870cb..197c5a657371 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -16,19 +16,25 @@
package android.service.carrier;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.telephony.SmsMessage;
import com.android.internal.util.Preconditions;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* Provides basic structure for platform to connect to the carrier messaging service.
@@ -46,6 +52,7 @@ import java.util.List;
* CarrierMessagingService.
* @hide
*/
+@SystemApi
public final class CarrierMessagingServiceWrapper {
// Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
// prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
@@ -53,6 +60,7 @@ public final class CarrierMessagingServiceWrapper {
private volatile ICarrierMessagingService mICarrierMessagingService;
private Runnable mOnServiceReadyCallback;
+ private Executor mServiceReadyCallbackExecutor;
/**
* Binds to the carrier messaging service under package {@code carrierPackageName}. This method
@@ -60,18 +68,27 @@ public final class CarrierMessagingServiceWrapper {
*
* @param context the context
* @param carrierPackageName the carrier package name
+ * @param executor the executor to run the callback.
+ * @param onServiceReadyCallback the callback when service becomes ready.
* @return true upon successfully binding to a carrier messaging service, false otherwise
* @hide
*/
+ @SystemApi
public boolean bindToCarrierMessagingService(@NonNull Context context,
@NonNull String carrierPackageName,
+ @NonNull @CallbackExecutor Executor executor,
@NonNull Runnable onServiceReadyCallback) {
Preconditions.checkState(mCarrierMessagingServiceConnection == null);
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(carrierPackageName);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(onServiceReadyCallback);
Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
intent.setPackage(carrierPackageName);
mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
mOnServiceReadyCallback = onServiceReadyCallback;
+ mServiceReadyCallbackExecutor = executor;
return context.bindService(intent, mCarrierMessagingServiceConnection,
Context.BIND_AUTO_CREATE);
}
@@ -82,11 +99,13 @@ public final class CarrierMessagingServiceWrapper {
* @param context the context
* @hide
*/
+ @SystemApi
public void disposeConnection(@NonNull Context context) {
Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
context.unbindService(mCarrierMessagingServiceConnection);
mCarrierMessagingServiceConnection = null;
mOnServiceReadyCallback = null;
+ mServiceReadyCallbackExecutor = null;
}
/**
@@ -96,26 +115,38 @@ public final class CarrierMessagingServiceWrapper {
*/
private void onServiceReady(ICarrierMessagingService carrierMessagingService) {
mICarrierMessagingService = carrierMessagingService;
- if (mOnServiceReadyCallback != null) mOnServiceReadyCallback.run();
+ if (mOnServiceReadyCallback != null && mServiceReadyCallbackExecutor != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mServiceReadyCallbackExecutor.execute(mOnServiceReadyCallback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
/**
- * Request filtering an incoming SMS message.
+ * Request the CarrierMessagingService to process an incoming SMS text or data message.
* The service will call callback.onFilterComplete with the filtering result.
*
* @param pdu the PDUs of the message
- * @param format the format of the PDUs, typically "3gpp" or "3gpp2"
+ * @param format the format of the PDUs, typically "3gpp" or "3gpp2".
+ * See {@link SmsMessage#FORMAT_3GPP} and {@link SmsMessage#FORMAT_3GPP2} for
+ * more details.
* @param destPort the destination port of a data SMS. It will be -1 for text SMS
* @param subId SMS subscription ID of the SIM
+ * @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
- public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
- int subId, @NonNull final CarrierMessagingCallback callback) {
+ @SystemApi
+ public void receiveSms(@NonNull MessagePdu pdu, @NonNull @SmsMessage.Format String format,
+ int destPort, int subId, @NonNull @CallbackExecutor final Executor executor,
+ @NonNull final CarrierMessagingCallback callback) {
if (mICarrierMessagingService != null) {
try {
mICarrierMessagingService.filterSms(pdu, format, destPort, subId,
- new CarrierMessagingCallbackInternal(callback));
+ new CarrierMessagingCallbackInternal(callback, executor));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -130,19 +161,23 @@ public final class CarrierMessagingServiceWrapper {
* @param text the text to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
- * @param sendSmsFlag flag for sending SMS
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
+ @SystemApi
public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
- int sendSmsFlag, @NonNull final CarrierMessagingCallback callback) {
- if (mICarrierMessagingService != null) {
- try {
- mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag,
- new CarrierMessagingCallbackInternal(callback));
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ @CarrierMessagingService.SendRequest int sendSmsFlag,
+ @NonNull @CallbackExecutor final Executor executor,
+ @NonNull final CarrierMessagingCallback callback) {
+ Objects.requireNonNull(mICarrierMessagingService);
+ try {
+ mICarrierMessagingService.sendTextSms(text, subId, destAddress, sendSmsFlag,
+ new CarrierMessagingCallbackInternal(callback, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -155,20 +190,24 @@ public final class CarrierMessagingServiceWrapper {
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param destPort port number of the recipient of the message
- * @param sendSmsFlag flag for sending SMS
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
+ @SystemApi
public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
- int destPort, int sendSmsFlag,
+ int destPort, @CarrierMessagingService.SendRequest int sendSmsFlag,
+ @NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
- if (mICarrierMessagingService != null) {
- try {
- mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort,
- sendSmsFlag, new CarrierMessagingCallbackInternal(callback));
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ Objects.requireNonNull(mICarrierMessagingService);
+ try {
+ mICarrierMessagingService.sendDataSms(data, subId, destAddress, destPort,
+ sendSmsFlag, new CarrierMessagingCallbackInternal(
+ callback, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -180,20 +219,24 @@ public final class CarrierMessagingServiceWrapper {
* @param parts the parts of the multi-part text SMS to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
- * @param sendSmsFlag flag for sending SMS
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link CarrierMessagingService#SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
+ @SystemApi
public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
- @NonNull String destAddress, int sendSmsFlag,
+ @NonNull String destAddress,
+ @CarrierMessagingService.SendRequest int sendSmsFlag,
+ @NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
- if (mICarrierMessagingService != null) {
- try {
- mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress,
- sendSmsFlag, new CarrierMessagingCallbackInternal(callback));
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ Objects.requireNonNull(mICarrierMessagingService);
+ try {
+ mICarrierMessagingService.sendMultipartTextSms(parts, subId, destAddress,
+ sendSmsFlag, new CarrierMessagingCallbackInternal(callback, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -206,18 +249,20 @@ public final class CarrierMessagingServiceWrapper {
* @param subId SMS subscription ID of the SIM
* @param location the optional URI to send this MMS PDU. If this is {code null},
* the PDU should be sent to the default MMSC URL.
+ * @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
+ @SystemApi
public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
+ @NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
- if (mICarrierMessagingService != null) {
- try {
- mICarrierMessagingService.sendMms(pduUri, subId, location,
- new CarrierMessagingCallbackInternal(callback));
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ Objects.requireNonNull(mICarrierMessagingService);
+ try {
+ mICarrierMessagingService.sendMms(pduUri, subId, location,
+ new CarrierMessagingCallbackInternal(callback, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -229,18 +274,20 @@ public final class CarrierMessagingServiceWrapper {
* @param pduUri the content provider URI of the PDU to be downloaded.
* @param subId SMS subscription ID of the SIM
* @param location the URI of the message to be downloaded.
+ * @param executor the executor to run the callback.
* @param callback the callback to notify upon completion
* @hide
*/
+ @SystemApi
public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
+ @NonNull @CallbackExecutor final Executor executor,
@NonNull final CarrierMessagingCallback callback) {
- if (mICarrierMessagingService != null) {
- try {
- mICarrierMessagingService.downloadMms(pduUri, subId, location,
- new CarrierMessagingCallbackInternal(callback));
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ Objects.requireNonNull(mICarrierMessagingService);
+ try {
+ mICarrierMessagingService.downloadMms(pduUri, subId, location,
+ new CarrierMessagingCallbackInternal(callback, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
}
}
@@ -263,19 +310,19 @@ public final class CarrierMessagingServiceWrapper {
* {@link CarrierMessagingServiceWrapper}.
* @hide
*/
+ @SystemApi
public interface CarrierMessagingCallback {
-
/**
- * Response callback for {@link CarrierMessagingServiceWrapper#filterSms}.
+ * Response callback for {@link CarrierMessagingServiceWrapper#receiveSms}.
* @param result a bitmask integer to indicate how the incoming text SMS should be handled
* by the platform. Bits set can be
* {@link CarrierMessagingService#RECEIVE_OPTIONS_DROP} and
* {@link CarrierMessagingService#
* RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE}.
- * {@see CarrierMessagingService#onReceiveTextSms}.
- * @hide
+ * {@link CarrierMessagingService#onReceiveTextSms}.
*/
- default void onFilterComplete(int result) {
+ default void onReceiveSmsComplete(
+ @CarrierMessagingService.FilterCompleteResult int result) {
}
@@ -287,10 +334,9 @@ public final class CarrierMessagingServiceWrapper {
* and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
* @param messageRef message reference of the just-sent message. This field is applicable
* only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
- * @hide
*/
- default void onSendSmsComplete(int result, int messageRef) {
-
+ default void onSendSmsComplete(@CarrierMessagingService.SendResult
+ int result, int messageRef) {
}
/**
@@ -301,9 +347,9 @@ public final class CarrierMessagingServiceWrapper {
* @param messageRefs an array of message references, one for each part of the
* multipart SMS. This field is applicable only if result is
* {@link CarrierMessagingService#SEND_STATUS_OK}.
- * @hide
*/
- default void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
+ default void onSendMultipartSmsComplete(@CarrierMessagingService.SendResult
+ int result, @Nullable int[] messageRefs) {
}
@@ -315,56 +361,62 @@ public final class CarrierMessagingServiceWrapper {
* @param sendConfPdu a possibly {code null} SendConf PDU, which confirms that the message
* was sent. sendConfPdu is ignored if the {@code result} is not
* {@link CarrierMessagingService#SEND_STATUS_OK}.
- * @hide
*/
- default void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
+ default void onSendMmsComplete(@CarrierMessagingService.SendResult
+ int result, @Nullable byte[] sendConfPdu) {
}
/**
* Response callback for {@link CarrierMessagingServiceWrapper#downloadMms}.
- * @param result download status, one of {@link CarrierMessagingService#SEND_STATUS_OK},
- * {@link CarrierMessagingService#SEND_STATUS_RETRY_ON_CARRIER_NETWORK},
- * and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
- * @hide
+ * @param result download status, one of {@link CarrierMessagingService#DOWNLOAD_STATUS_OK},
+ * {@link CarrierMessagingService#DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK},
+ * and {@link CarrierMessagingService#DOWNLOAD_STATUS_ERROR}.
*/
- default void onDownloadMmsComplete(int result) {
+ default void onDownloadMmsComplete(@CarrierMessagingService.DownloadResult
+ int result) {
}
}
private final class CarrierMessagingCallbackInternal
extends ICarrierMessagingCallback.Stub {
- CarrierMessagingCallback mCarrierMessagingCallback;
+ final CarrierMessagingCallback mCarrierMessagingCallback;
+ final Executor mExecutor;
- CarrierMessagingCallbackInternal(CarrierMessagingCallback callback) {
+ CarrierMessagingCallbackInternal(CarrierMessagingCallback callback,
+ final Executor executor) {
mCarrierMessagingCallback = callback;
+ mExecutor = executor;
}
@Override
public void onFilterComplete(int result) throws RemoteException {
- mCarrierMessagingCallback.onFilterComplete(result);
+ mExecutor.execute(() -> mCarrierMessagingCallback.onReceiveSmsComplete(result));
}
@Override
public void onSendSmsComplete(int result, int messageRef) throws RemoteException {
- mCarrierMessagingCallback.onSendSmsComplete(result, messageRef);
+ mExecutor.execute(() -> mCarrierMessagingCallback.onSendSmsComplete(
+ result, messageRef));
}
@Override
public void onSendMultipartSmsComplete(int result, int[] messageRefs)
throws RemoteException {
- mCarrierMessagingCallback.onSendMultipartSmsComplete(result, messageRefs);
+ mExecutor.execute(() -> mCarrierMessagingCallback.onSendMultipartSmsComplete(
+ result, messageRefs));
}
@Override
public void onSendMmsComplete(int result, byte[] sendConfPdu) throws RemoteException {
- mCarrierMessagingCallback.onSendMmsComplete(result, sendConfPdu);
+ mExecutor.execute(() -> mCarrierMessagingCallback.onSendMmsComplete(
+ result, sendConfPdu));
}
@Override
public void onDownloadMmsComplete(int result) throws RemoteException {
- mCarrierMessagingCallback.onDownloadMmsComplete(result);
+ mExecutor.execute(() -> mCarrierMessagingCallback.onDownloadMmsComplete(result));
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index aa9e289345db..440eeb1619f0 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1517,8 +1517,10 @@ public abstract class NotificationListenerService extends Service {
*/
public static class Ranking {
- /** Value signifying that the user has not expressed a per-app visibility override value.
- * @hide */
+ /**
+ * Value signifying that the user and device policy manager have not expressed a lockscreen
+ * visibility override for a notification.
+ */
public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
/**
@@ -1695,14 +1697,13 @@ public abstract class NotificationListenerService extends Service {
}
/**
- * Returns the user specified visibility for the package that posted
- * this notification, or
+ * Returns the user or device policy manager specified visibility (see
+ * {@link Notification#VISIBILITY_PRIVATE}, {@link Notification#VISIBILITY_PUBLIC},
+ * {@link Notification#VISIBILITY_SECRET}) for this notification, or
* {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
* no such preference has been expressed.
- * @hide
*/
- @UnsupportedAppUsage
- public int getVisibilityOverride() {
+ public int getLockscreenVisibilityOverride() {
return mVisibilityOverride;
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 787a81bac3c0..12d905588e1e 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -45,13 +45,14 @@ import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -515,7 +516,7 @@ public class ZenModeConfig implements Parcelable {
}
}
- public static ZenModeConfig readXml(XmlPullParser parser)
+ public static ZenModeConfig readXml(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
int type = parser.getEventType();
if (type != XmlPullParser.START_TAG) return null;
@@ -611,28 +612,28 @@ public class ZenModeConfig implements Parcelable {
* @param version uses XML_VERSION if version is null
* @throws IOException
*/
- public void writeXml(XmlSerializer out, Integer version) throws IOException {
+ public void writeXml(TypedXmlSerializer out, Integer version) throws IOException {
out.startTag(null, ZEN_TAG);
out.attribute(null, ZEN_ATT_VERSION, version == null
? Integer.toString(XML_VERSION) : Integer.toString(version));
- out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
+ out.attributeInt(null, ZEN_ATT_USER, user);
out.startTag(null, ALLOW_TAG);
- out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
- out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
- out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
- out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
- out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
- out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
- out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
- out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
- out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
- out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
- out.attribute(null, ALLOW_ATT_CONV, Boolean.toString(allowConversations));
- out.attribute(null, ALLOW_ATT_CONV_FROM, Integer.toString(allowConversationsFrom));
+ out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls);
+ out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers);
+ out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages);
+ out.attributeBoolean(null, ALLOW_ATT_REMINDERS, allowReminders);
+ out.attributeBoolean(null, ALLOW_ATT_EVENTS, allowEvents);
+ out.attributeInt(null, ALLOW_ATT_CALLS_FROM, allowCallsFrom);
+ out.attributeInt(null, ALLOW_ATT_MESSAGES_FROM, allowMessagesFrom);
+ out.attributeBoolean(null, ALLOW_ATT_ALARMS, allowAlarms);
+ out.attributeBoolean(null, ALLOW_ATT_MEDIA, allowMedia);
+ out.attributeBoolean(null, ALLOW_ATT_SYSTEM, allowSystem);
+ out.attributeBoolean(null, ALLOW_ATT_CONV, allowConversations);
+ out.attributeInt(null, ALLOW_ATT_CONV_FROM, allowConversationsFrom);
out.endTag(null, ALLOW_TAG);
out.startTag(null, DISALLOW_TAG);
- out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects));
+ out.attributeInt(null, DISALLOW_ATT_VISUAL_EFFECTS, suppressedVisualEffects);
out.endTag(null, DISALLOW_TAG);
if (manualRule != null) {
@@ -651,14 +652,13 @@ public class ZenModeConfig implements Parcelable {
}
out.startTag(null, STATE_TAG);
- out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND,
- Boolean.toString(areChannelsBypassingDnd));
+ out.attributeBoolean(null, STATE_ATT_CHANNELS_BYPASSING_DND, areChannelsBypassingDnd);
out.endTag(null, STATE_TAG);
out.endTag(null, ZEN_TAG);
}
- public static ZenRule readRuleXml(XmlPullParser parser) {
+ public static ZenRule readRuleXml(TypedXmlPullParser parser) {
final ZenRule rt = new ZenRule();
rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
@@ -691,12 +691,12 @@ public class ZenModeConfig implements Parcelable {
return rt;
}
- public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
- out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
+ public static void writeRuleXml(ZenRule rule, TypedXmlSerializer out) throws IOException {
+ out.attributeBoolean(null, RULE_ATT_ENABLED, rule.enabled);
if (rule.name != null) {
out.attribute(null, RULE_ATT_NAME, rule.name);
}
- out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
+ out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode);
if (rule.component != null) {
out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
}
@@ -707,7 +707,7 @@ public class ZenModeConfig implements Parcelable {
if (rule.conditionId != null) {
out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
}
- out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
+ out.attributeLong(null, RULE_ATT_CREATION_TIME, rule.creationTime);
if (rule.enabler != null) {
out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
}
@@ -717,10 +717,10 @@ public class ZenModeConfig implements Parcelable {
if (rule.zenPolicy != null) {
writeZenPolicyXml(rule.zenPolicy, out);
}
- out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified));
+ out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified);
}
- public static Condition readConditionXml(XmlPullParser parser) {
+ public static Condition readConditionXml(TypedXmlPullParser parser) {
final Uri id = safeUri(parser, CONDITION_ATT_ID);
if (id == null) return null;
final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
@@ -737,21 +737,21 @@ public class ZenModeConfig implements Parcelable {
}
}
- public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
+ public static void writeConditionXml(Condition c, TypedXmlSerializer out) throws IOException {
out.attribute(null, CONDITION_ATT_ID, c.id.toString());
out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
out.attribute(null, CONDITION_ATT_LINE1, c.line1);
out.attribute(null, CONDITION_ATT_LINE2, c.line2);
- out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
- out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
- out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
+ out.attributeInt(null, CONDITION_ATT_ICON, c.icon);
+ out.attributeInt(null, CONDITION_ATT_STATE, c.state);
+ out.attributeInt(null, CONDITION_ATT_FLAGS, c.flags);
}
/**
* Read the zen policy from xml
* Returns null if no zen policy exists
*/
- public static ZenPolicy readZenPolicyXml(XmlPullParser parser) {
+ public static ZenPolicy readZenPolicyXml(TypedXmlPullParser parser) {
boolean policySet = false;
ZenPolicy.Builder builder = new ZenPolicy.Builder();
@@ -845,7 +845,7 @@ public class ZenModeConfig implements Parcelable {
/**
* Writes zen policy to xml
*/
- public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out)
+ public static void writeZenPolicyXml(ZenPolicy policy, TypedXmlSerializer out)
throws IOException {
writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out);
writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out);
@@ -868,16 +868,16 @@ public class ZenModeConfig implements Parcelable {
out);
}
- private static void writeZenPolicyState(String attr, int val, XmlSerializer out)
+ private static void writeZenPolicyState(String attr, int val, TypedXmlSerializer out)
throws IOException {
if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM)
|| Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) {
if (val != ZenPolicy.PEOPLE_TYPE_UNSET) {
- out.attribute(null, attr, Integer.toString(val));
+ out.attributeInt(null, attr, val);
}
} else {
if (val != ZenPolicy.STATE_UNSET) {
- out.attribute(null, attr, Integer.toString(val));
+ out.attributeInt(null, attr, val);
}
}
}
@@ -894,15 +894,16 @@ public class ZenModeConfig implements Parcelable {
return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
}
- private static Boolean unsafeBoolean(XmlPullParser parser, String att) {
- final String val = parser.getAttributeValue(null, att);
- if (TextUtils.isEmpty(val)) return null;
- return Boolean.parseBoolean(val);
+ private static Boolean unsafeBoolean(TypedXmlPullParser parser, String att) {
+ try {
+ return parser.getAttributeBoolean(null, att);
+ } catch (Exception e) {
+ return null;
+ }
}
- private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
- final String val = parser.getAttributeValue(null, att);
- return safeBoolean(val, defValue);
+ private static boolean safeBoolean(TypedXmlPullParser parser, String att, boolean defValue) {
+ return parser.getAttributeBoolean(null, att, defValue);
}
private static boolean safeBoolean(String val, boolean defValue) {
@@ -910,24 +911,23 @@ public class ZenModeConfig implements Parcelable {
return Boolean.parseBoolean(val);
}
- private static int safeInt(XmlPullParser parser, String att, int defValue) {
- final String val = parser.getAttributeValue(null, att);
- return tryParseInt(val, defValue);
+ private static int safeInt(TypedXmlPullParser parser, String att, int defValue) {
+ return parser.getAttributeInt(null, att, defValue);
}
- private static ComponentName safeComponentName(XmlPullParser parser, String att) {
+ private static ComponentName safeComponentName(TypedXmlPullParser parser, String att) {
final String val = parser.getAttributeValue(null, att);
if (TextUtils.isEmpty(val)) return null;
return ComponentName.unflattenFromString(val);
}
- private static Uri safeUri(XmlPullParser parser, String att) {
+ private static Uri safeUri(TypedXmlPullParser parser, String att) {
final String val = parser.getAttributeValue(null, att);
if (TextUtils.isEmpty(val)) return null;
return Uri.parse(val);
}
- private static long safeLong(XmlPullParser parser, String att, long defValue) {
+ private static long safeLong(TypedXmlPullParser parser, String att, long defValue) {
final String val = parser.getAttributeValue(null, att);
return tryParseLong(val, defValue);
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 479a0c16c747..ac6208b1714c 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -17,7 +17,10 @@
package android.telephony;
import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.compat.annotation.ChangeId;
@@ -28,17 +31,27 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.telephony.Annotation.CallState;
+import android.telephony.Annotation.DataActivityType;
+import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.PreciseDisconnectCauses;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.NetworkRegistrationInfo.Domain;
+import android.telephony.TelephonyManager.DataEnabledReason;
+import android.telephony.TelephonyManager.DataState;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IPhoneStateListener;
import dalvik.system.VMRuntime;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
@@ -113,7 +126,9 @@ public class PhoneStateListener {
*
* @see #onServiceStateChanged
* @see ServiceState
+ * @deprecated Use {@link ServiceStateChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_SERVICE_STATE = 0x00000001;
/**
@@ -121,8 +136,7 @@ public class PhoneStateListener {
* {@more}
*
* @see #onSignalStrengthChanged
- *
- * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS}
+ * @deprecated Use {@link SignalStrengthsChangedListener} instead.
*/
@Deprecated
public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002;
@@ -138,7 +152,9 @@ public class PhoneStateListener {
* voicemail icon.
*
* @see #onMessageWaitingIndicatorChanged
+ * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004;
/**
@@ -149,7 +165,9 @@ public class PhoneStateListener {
* {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onCallForwardingIndicatorChanged
+ * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008;
/**
@@ -165,7 +183,9 @@ public class PhoneStateListener {
* instead.
*
* @see #onCellLocationChanged
+ * @deprecated Use {@link CellLocationChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_CELL_LOCATION = 0x00000010;
/**
@@ -173,14 +193,18 @@ public class PhoneStateListener {
* {@more}
*
* @see #onCallStateChanged
+ * @deprecated Use {@link CallStateChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_CALL_STATE = 0x00000020;
/**
* Listen for changes to the data connection state (cellular).
*
* @see #onDataConnectionStateChanged
+ * @deprecated Use {@link DataConnectionStateChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040;
/**
@@ -191,7 +215,9 @@ public class PhoneStateListener {
* data-traffic icon.
*
* @see #onDataActivity
+ * @deprecated Use {@link DataActivityListener} instead.
*/
+ @Deprecated
public static final int LISTEN_DATA_ACTIVITY = 0x00000080;
/**
@@ -201,7 +227,9 @@ public class PhoneStateListener {
* icon.
*
* @see #onSignalStrengthsChanged
+ * @deprecated Use {@link SignalStrengthsChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
/**
@@ -211,7 +239,9 @@ public class PhoneStateListener {
* @see #onSignalStrengthsChanged
*
* @hide
+ * @deprecated Use {@link AlwaysReportedSignalStrengthChangedListener} instead.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200;
@@ -222,7 +252,9 @@ public class PhoneStateListener {
* permission.
*
* @see #onCellInfoChanged
+ * @deprecated Use {@link CellInfoChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_CELL_INFO = 0x00000400;
/**
@@ -234,8 +266,10 @@ public class PhoneStateListener {
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @hide
+ * @deprecated Use {@link PreciseCallStateChangedListener} instead.
*/
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@SystemApi
public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
@@ -247,8 +281,10 @@ public class PhoneStateListener {
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onPreciseDataConnectionStateChanged
+ * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead.
*/
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
/**
@@ -258,7 +294,7 @@ public class PhoneStateListener {
* READ_PRECISE_PHONE_STATE}
* @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
*
- * @deprecated Use {@link TelephonyManager#getModemActivityInfo()}
+ * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} instead.
* @hide
*/
@Deprecated
@@ -271,7 +307,9 @@ public class PhoneStateListener {
*
* @see #onServiceStateChanged(ServiceState)
* @hide
+ * @deprecated Use {@link SrvccStateChangedListener} instead.
*/
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public static final int LISTEN_SRVCC_STATE_CHANGED = 0x00004000;
@@ -289,10 +327,11 @@ public class PhoneStateListener {
/**
* Listen for carrier network changes indicated by a carrier app.
*
- * @see #onCarrierNetworkRequest
- * @see TelephonyManager#notifyCarrierNetworkChange(boolean)
+ * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
* @hide
+ * @deprecated Use {@link CarrierNetworkChangeListener} instead.
*/
+ @Deprecated
public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000;
/**
@@ -311,7 +350,9 @@ public class PhoneStateListener {
*
* @see #onVoiceActivationStateChanged
* @hide
+ * @deprecated Use {@link VoiceActivationStateChangedListener} instead.
*/
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000;
@@ -323,20 +364,24 @@ public class PhoneStateListener {
* @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
* @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
* @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- * {@more}
+ *
* Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
* fully activated
*
* @see #onDataActivationStateChanged
* @hide
+ * @deprecated Use {@link DataActivationStateChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
/**
* Listen for changes to the user mobile data state
*
* @see #onUserMobileDataStateChanged
+ * @deprecated Use {@link UserMobileDataStateChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
/**
@@ -347,7 +392,9 @@ public class PhoneStateListener {
* {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onDisplayInfoChanged
+ * @deprecated Use {@link DisplayInfoChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000;
/**
@@ -355,7 +402,9 @@ public class PhoneStateListener {
*
* @see #onPhoneCapabilityChanged
* @hide
+ * @deprecated Use {@link PhoneCapabilityChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000;
/**
@@ -365,17 +414,19 @@ public class PhoneStateListener {
* subscription user selected as default data subscription in DSDS mode.
*
* @see #onActiveDataSubscriptionIdChanged
+ * @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
/**
* Listen for changes to the radio power state.
*
- * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
- *
* @see #onRadioPowerStateChanged
* @hide
+ * @deprecated Use {@link RadioPowerStateChangedListener} instead.
*/
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 0x00800000;
@@ -385,7 +436,10 @@ public class PhoneStateListener {
*
* <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
* app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @deprecated Use {@link EmergencyNumberListChangedListener} instead.
*/
+ @Deprecated
public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
/**
@@ -396,8 +450,10 @@ public class PhoneStateListener {
* or the calling app has carrier privileges
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
+ * @deprecated Use {@link CallDisconnectCauseChangedListener} instead.
*/
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
/**
@@ -409,9 +465,11 @@ public class PhoneStateListener {
*
* @see #onCallAttributesChanged
* @hide
+ * @deprecated Use {@link CallAttributesChangedListener} instead.
*/
+ @Deprecated
@SystemApi
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000;
/**
@@ -423,18 +481,20 @@ public class PhoneStateListener {
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
+ * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead.
*/
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000;
/**
* Listen for the emergency number placed from an outgoing call.
*
- * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
- *
* @see #onOutgoingEmergencyCall
* @hide
+ * @deprecated Use {@link OutgoingEmergencyCallListener} instead.
*/
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 0x10000000;
@@ -442,11 +502,11 @@ public class PhoneStateListener {
/**
* Listen for the emergency number placed from an outgoing SMS.
*
- * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
- *
* @see #onOutgoingEmergencySms
* @hide
+ * @deprecated Use {@link OutgoingEmergencySmsListener} instead.
*/
+ @Deprecated
@SystemApi
@RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000;
@@ -465,7 +525,9 @@ public class PhoneStateListener {
* of whether the calling app has carrier privileges.
*
* @see #onRegistrationFailed
+ * @deprecated Use {@link RegistrationFailedListener} instead.
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
@@ -479,19 +541,525 @@ public class PhoneStateListener {
* of whether the calling app has carrier privileges.
*
* @see #onBarringInfoChanged
+ * @deprecated Use {@link BarringInfoChangedListener} instead.
*/
+ @Deprecated
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public static final int LISTEN_BARRING_INFO = 0x80000000;
/**
- * Listen for changes to the physical channel configuration.
+ * Event for changes to the network service state (cellular).
+ *
+ * @see ServiceStateChangedListener#onServiceStateChanged
+ * @see ServiceState
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_SERVICE_STATE_CHANGED = 1;
+
+ /**
+ * Event for changes to the network signal strength (cellular).
+ *
+ * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2;
+
+ /**
+ * Event for changes to the message-waiting indicator.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+ * the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ * <p>
+ * Example: The status bar uses this to determine when to display the
+ * voicemail icon.
+ *
+ * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3;
+
+ /**
+ * Event for changes to the call-forwarding indicator.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that
+ * the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4;
+
+ /**
+ * Event for changes to the device's cell location. Note that
+ * this will result in frequent callbacks to the listener.
+ *
+ * If you need regular location updates but want more control over
+ * the update interval or location precision, you can set up a listener
+ * through the {@link android.location.LocationManager location manager}
+ * instead.
+ *
+ * @see CellLocationChangedListener#onCellLocationChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public static final int EVENT_CELL_LOCATION_CHANGED = 5;
+
+ /**
+ * Event for changes to the device call state.
+ *
+ * @see CallStateChangedListener#onCallStateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+ public static final int EVENT_CALL_STATE_CHANGED = 6;
+
+ /**
+ * Event for changes to the data connection state (cellular).
+ *
+ * @see DataConnectionStateChangedListener#onDataConnectionStateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7;
+
+ /**
+ * Event for changes to the direction of data traffic on the data
+ * connection (cellular).
+ *
+ * Example: The status bar uses this to display the appropriate
+ * data-traffic icon.
+ *
+ * @see DataActivityListener#onDataActivity
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_DATA_ACTIVITY_CHANGED = 8;
+
+ /**
+ * Event for changes to the network signal strengths (cellular).
+ * <p>
+ * Example: The status bar uses this to control the signal-strength
+ * icon.
+ *
+ * @see SignalStrengthsChangedListener#onSignalStrengthsChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9;
+
+ /**
+ * Event for changes of the network signal strengths (cellular) always reported from modem,
+ * even in some situations such as the screen of the device is off.
+ *
+ * @see AlwaysReportedSignalStrengthChangedListener#onSignalStrengthsChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+ public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
+
+ /**
+ * Event for changes to observed cell info.
+ *
+ * @see CellInfoChangedListener#onCellInfoChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public static final int EVENT_CELL_INFO_CHANGED = 11;
+
+ /**
+ * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
+ * background and foreground calls.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see PreciseCallStateChangedListener#onPreciseCallStateChanged
*
- * @see #onPhysicalChannelConfigurationChanged
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
- public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x100000000L;
+ public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12;
+
+ /**
+ * Event for {@link PreciseDataConnectionState} on the data connection (cellular).
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13;
+
+ /**
+ * Event for real time info for all data connections (cellular)).
+ *
+ * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+ *
+ * @deprecated Use {@link TelephonyManager#requestModemActivityInfo}
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14;
+
+ /**
+ * Event for OEM hook raw event
+ *
+ * @see #onOemHookRawEvent
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_OEM_HOOK_RAW = 15;
+
+ /**
+ * Event for changes to the SRVCC state of the active call.
+ *
+ * @see SrvccStateChangedListener#onSrvccStateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_SRVCC_STATE_CHANGED = 16;
+
+ /**
+ * Event for carrier network changes indicated by a carrier app.
+ *
+ * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)
+ * @see CarrierNetworkChangeListener#onCarrierNetworkChange
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_CARRIER_NETWORK_CHANGED = 17;
+
+ /**
+ * Event for changes to the sim voice activation state
+ *
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ *
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+ * fully activated
+ *
+ * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18;
+
+ /**
+ * Event for changes to the sim data activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ *
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+ * fully activated
+ *
+ * @see DataActivationStateChangedListener#onDataActivationStateChanged
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19;
+
+ /**
+ * Event for changes to the user mobile data state
+ *
+ * @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20;
+
+ /**
+ * Event for display info changed event.
+ *
+ * @see DisplayInfoChangedListener#onDisplayInfoChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_DISPLAY_INFO_CHANGED = 21;
+
+ /**
+ * Event for changes to the phone capability.
+ *
+ * @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22;
+
+ /**
+ * Event for changes to active data subscription ID. Active data subscription is
+ * the current subscription used to setup Cellular Internet data. For example,
+ * it could be the current active opportunistic subscription in use, or the
+ * subscription user selected as default data subscription in DSDS mode.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23;
+
+ /**
+ * Event for changes to the radio power state.
+ *
+ * @see RadioPowerStateChangedListener#onRadioPowerStateChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24;
+
+ /**
+ * Event for changes to emergency number list based on all active subscriptions.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25;
+
+ /**
+ * Event for call disconnect causes which contains {@link DisconnectCause} and
+ * {@link PreciseDisconnectCause}.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26;
+
+ /**
+ * Event for changes to the call attributes of a currently active call.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see CallAttributesChangedListener#onCallAttributesChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27;
+
+ /**
+ * Event for IMS call disconnect causes which contains
+ * {@link android.telephony.ims.ImsReasonInfo}
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo)
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28;
+
+ /**
+ * Event for the emergency number placed from an outgoing call.
+ *
+ * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29;
+
+ /**
+ * Event for the emergency number placed from an outgoing SMS.
+ *
+ * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30;
+
+ /**
+ * Event for registration failures.
+ *
+ * Event for indications that a registration procedure has failed in either the CS or PS
+ * domain. This indication does not necessarily indicate a change of service state, which should
+ * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+ * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
+ * @see RegistrationFailedListener#onRegistrationFailed
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public static final int EVENT_REGISTRATION_FAILURE = 31;
+
+ /**
+ * Event for Barring Information for the current registered / camped cell.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+ * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
+ * @see BarringInfoChangedListener#onBarringInfoChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public static final int EVENT_BARRING_INFO_CHANGED = 32;
+
+ /**
+ * Event for changes to the physical channel configuration.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33;
+
+
+ /**
+ * Event for changes to the data enabled.
+ *
+ * Event for indications that the enabled status of current data has changed.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see DataEnabledChangedListener#onDataEnabledChanged
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public static final int EVENT_DATA_ENABLED_CHANGED = 34;
+
+ /** @hide */
+ @IntDef(prefix = { "EVENT_" }, value = {
+ EVENT_SERVICE_STATE_CHANGED,
+ EVENT_SIGNAL_STRENGTH_CHANGED,
+ EVENT_MESSAGE_WAITING_INDICATOR_CHANGED,
+ EVENT_CALL_FORWARDING_INDICATOR_CHANGED,
+ EVENT_CELL_LOCATION_CHANGED,
+ EVENT_CALL_STATE_CHANGED,
+ EVENT_DATA_CONNECTION_STATE_CHANGED,
+ EVENT_DATA_ACTIVITY_CHANGED,
+ EVENT_SIGNAL_STRENGTHS_CHANGED,
+ EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED,
+ EVENT_CELL_INFO_CHANGED,
+ EVENT_PRECISE_CALL_STATE_CHANGED,
+ EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED,
+ EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED,
+ EVENT_OEM_HOOK_RAW,
+ EVENT_SRVCC_STATE_CHANGED,
+ EVENT_CARRIER_NETWORK_CHANGED,
+ EVENT_VOICE_ACTIVATION_STATE_CHANGED,
+ EVENT_DATA_ACTIVATION_STATE_CHANGED,
+ EVENT_USER_MOBILE_DATA_STATE_CHANGED,
+ EVENT_DISPLAY_INFO_CHANGED,
+ EVENT_PHONE_CAPABILITY_CHANGED,
+ EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED,
+ EVENT_RADIO_POWER_STATE_CHANGED,
+ EVENT_EMERGENCY_NUMBER_LIST_CHANGED,
+ EVENT_CALL_DISCONNECT_CAUSE_CHANGED,
+ EVENT_CALL_ATTRIBUTES_CHANGED,
+ EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED,
+ EVENT_OUTGOING_EMERGENCY_CALL,
+ EVENT_OUTGOING_EMERGENCY_SMS,
+ EVENT_REGISTRATION_FAILURE,
+ EVENT_BARRING_INFO_CHANGED,
+ EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED,
+ EVENT_DATA_ENABLED_CHANGED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TelephonyEvent {}
/*
* Subscription used to listen to the phone state changes
@@ -504,9 +1072,13 @@ public class PhoneStateListener {
/**
* @hide
*/
+ //TODO: The maxTargetSdk should be S if the build time tool updates it.
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- @UnsupportedAppUsage
- public final IPhoneStateListener callback;
+ @UnsupportedAppUsage(
+ maxTargetSdk = Build.VERSION_CODES.R,
+ publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" +
+ "Executor, PhoneStateListener)} instead")
+ public IPhoneStateListener callback;
/**
* Create a PhoneStateListener for the Phone with the default subscription.
@@ -563,17 +1135,742 @@ public class PhoneStateListener {
* The Executor must not be null.
*
* @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
+ * @deprecated Use
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead.
*/
+ @Deprecated
public PhoneStateListener(@NonNull Executor executor) {
this(null, executor);
}
- private PhoneStateListener(Integer subId, Executor e) {
- if (e == null) {
+ private @NonNull Executor mExecutor;
+
+ /**
+ * @hide
+ */
+ public void setExecutor(@NonNull @CallbackExecutor Executor executor) {
+ if (executor == null) {
throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
}
+ mExecutor = executor;
+ }
+
+ private PhoneStateListener(Integer subId, Executor executor) {
+ setExecutor(executor);
mSubId = subId;
- callback = new IPhoneStateListenerStub(this, e);
+ callback = new IPhoneStateListenerStub(this, mExecutor);
+ }
+
+ /**
+ * Interface for service state listener.
+ */
+ public interface ServiceStateChangedListener {
+ /**
+ * Callback invoked when device service state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * The instance of {@link ServiceState} passed as an argument here will have various
+ * levels of location information stripped from it depending on the location permissions
+ * that your app holds.
+ * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+ * receive all the information in {@link ServiceState}.
+ *
+ * @see ServiceState#STATE_EMERGENCY_ONLY
+ * @see ServiceState#STATE_IN_SERVICE
+ * @see ServiceState#STATE_OUT_OF_SERVICE
+ * @see ServiceState#STATE_POWER_OFF
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onServiceStateChanged(@NonNull ServiceState serviceState);
+ }
+
+ /**
+ * Interface for message waiting indicator listener.
+ */
+ public interface MessageWaitingIndicatorChangedListener {
+ /**
+ * Callback invoked when the message-waiting indicator changes on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onMessageWaitingIndicatorChanged(boolean mwi);
+ }
+
+ /**
+ * Interface for call-forwarding indicator listener.
+ */
+ public interface CallForwardingIndicatorChangedListener {
+ /**
+ * Callback invoked when the call-forwarding indicator changes on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onCallForwardingIndicatorChanged(boolean cfi);
+ }
+
+ /**
+ * Interface for device cell location listener.
+ */
+ public interface CellLocationChangedListener {
+ /**
+ * Callback invoked when device cell location changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public void onCellLocationChanged(@NonNull CellLocation location);
+ }
+
+ /**
+ * Interface for call state listener.
+ */
+ public interface CallStateChangedListener {
+ /**
+ * Callback invoked when device call state changes.
+ * <p>
+ * Reports the state of Telephony (mobile) calls on the device for the registered s
+ * ubscription.
+ * <p>
+ * Note: the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ * <p>
+ * Note: The state returned here may differ from that returned by
+ * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
+ * calling {@link TelephonyManager#getCallState()} from within this callback may return a
+ * different state than the callback reports.
+ *
+ * @param state call state
+ * @param phoneNumber call phone number. If application does not have
+ * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier
+ * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
+ * passed as an argument.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_CALL_LOG)
+ public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber);
+ }
+
+ /**
+ * Interface for data connection state listener.
+ */
+ public interface DataConnectionStateChangedListener {
+ /**
+ * Callback invoked when connection state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ *
+ * @param state is the current state of data connection.
+ * @param networkType is the current network type of data connection.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onDataConnectionStateChanged(@DataState int state,
+ @NetworkType int networkType);
+ }
+
+ /**
+ * Interface for data activity state listener.
+ */
+ public interface DataActivityListener {
+ /**
+ * Callback invoked when data activity state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @see TelephonyManager#DATA_ACTIVITY_NONE
+ * @see TelephonyManager#DATA_ACTIVITY_IN
+ * @see TelephonyManager#DATA_ACTIVITY_OUT
+ * @see TelephonyManager#DATA_ACTIVITY_INOUT
+ * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onDataActivity(@DataActivityType int direction);
+ }
+
+ /**
+ * Interface for network signal strengths listener.
+ */
+ public interface SignalStrengthsChangedListener {
+ /**
+ * Callback invoked when network signal strengths changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+ }
+
+ /**
+ * Interface for network signal strengths listener which always reported from modem.
+ */
+ public interface AlwaysReportedSignalStrengthChangedListener {
+ /**
+ * Callback always invoked from modem when network signal strengths changes on the
+ * registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
+ public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+ }
+
+ /**
+ * Interface for cell info listener.
+ */
+ public interface CellInfoChangedListener {
+ /**
+ * Callback invoked when a observed cell info has changed or new cells have been added
+ * or removed on the registered subscription.
+ * Note, the registration subscription ID s from {@link TelephonyManager} object
+ * which registersPhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param cellInfo is the list of currently visible cells.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+ public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
+ }
+
+ /**
+ * Interface for precise device call state listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface PreciseCallStateChangedListener {
+ /**
+ * Callback invoked when precise device call state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param callState {@link PreciseCallState}
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
+ }
+
+ /**
+ * Interface for call disconnect cause listener.
+ */
+ public interface CallDisconnectCauseChangedListener {
+ /**
+ * Callback invoked when call disconnect cause changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param disconnectCause {@link DisconnectCause}.
+ * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
+ @PreciseDisconnectCauses int preciseDisconnectCause);
+ }
+
+ /**
+ * Interface for IMS call disconnect cause listener.
+ */
+ public interface ImsCallDisconnectCauseChangedListener {
+ /**
+ * Callback invoked when IMS call disconnect cause changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
+ *
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
+ }
+
+ /**
+ * Interface for precise data connection state listener.
+ */
+ public interface PreciseDataConnectionStateChangedListener {
+ /**
+ * Callback providing update about the default/internet data connection on the registered
+ * subscription.
+ *
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param dataConnectionState {@link PreciseDataConnectionState}
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onPreciseDataConnectionStateChanged(
+ @NonNull PreciseDataConnectionState dataConnectionState);
+ }
+
+ /**
+ * Interface for Single Radio Voice Call Continuity listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface SrvccStateChangedListener {
+ /**
+ * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
+ * (SRVCC) state for the currently active call on the registered subscription.
+ *
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void onSrvccStateChanged(@SrvccState int srvccState);
+ }
+
+ /**
+ * Interface for SIM voice activation state listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface VoiceActivationStateChangedListener {
+ /**
+ * Callback invoked when the SIM voice activation state has changed on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state is the current SIM voice activation state
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void onVoiceActivationStateChanged(@SimActivationState int state);
+
+ }
+
+ /**
+ * Interface for SIM data activation state listener.
+ */
+ public interface DataActivationStateChangedListener {
+ /**
+ * Callback invoked when the SIM data activation state has changed on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state is the current SIM data activation state
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onDataActivationStateChanged(@SimActivationState int state);
+ }
+
+ /**
+ * Interface for user mobile data state listener.
+ */
+ public interface UserMobileDataStateChangedListener {
+ /**
+ * Callback invoked when the user mobile data state has changed on the registered
+ * subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param enabled indicates whether the current user mobile data state is enabled or
+ * disabled.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onUserMobileDataStateChanged(boolean enabled);
+ }
+
+ /**
+ * Interface for display info listener.
+ */
+ public interface DisplayInfoChangedListener {
+ /**
+ * Callback invoked when the display info has changed on the registered subscription.
+ * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user
+ * based on carrier policy.
+ *
+ * @param telephonyDisplayInfo The display information.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
+ }
+
+ /**
+ * Interface for the current emergency number list listener.
+ */
+ public interface EmergencyNumberListChangedListener {
+ /**
+ * Callback invoked when the current emergency number list has changed on the registered
+ * subscription.
+ *
+ * Note, the registered subscription is associated with {@link TelephonyManager} object
+ * on which
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}
+ * was called.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * given subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param emergencyNumberList Map associating all active subscriptions on the device with
+ * the list of emergency numbers originating from that
+ * subscription.
+ * If there are no active subscriptions, the map will contain a
+ * single entry with
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as
+ * the key and a list of emergency numbers as the value. If no
+ * emergency number information is available, the value will be
+ * empty.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onEmergencyNumberListChanged(
+ @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
+ }
+
+ /**
+ * Interface for outgoing emergency call listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface OutgoingEmergencyCallListener {
+ /**
+ * Callback invoked when an outgoing call is placed to an emergency number.
+ *
+ * This method will be called when an emergency call is placed on any subscription
+ * (including the no-SIM case), regardless of which subscription this listener was
+ * registered on.
+ *
+ * The default implementation of this method calls
+ * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes.
+ * Do not call {@code super(...)} from within your implementation unless you want
+ * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well.
+ *
+ * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was
+ * placed to.
+ * @param subscriptionId The subscription ID used to place the emergency call. If the
+ * emergency call was placed without a valid subscription
+ * (e.g. when there are no SIM cards in the device), this will be
+ * equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ */
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+ int subscriptionId);
+ }
+
+ /**
+ * Interface for outgoing emergency sms listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface OutgoingEmergencySmsListener {
+ /**
+ * Smsback invoked when an outgoing sms is sent to an emergency number.
+ *
+ * This method will be called when an emergency sms is sent on any subscription,
+ * regardless of which subscription this listener was registered on.
+ *
+ * The default implementation of this method calls
+ * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do
+ * not call {@code super(...)} from within your implementation unless you want
+ * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well.
+ *
+ * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
+ * @param subscriptionId The subscription ID used to send the emergency sms.
+ */
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+ int subscriptionId);
+ }
+
+ /**
+ * Interface for phone capability listener.
+ *
+ */
+ public interface PhoneCapabilityChangedListener {
+ /**
+ * Callback invoked when phone capability changes.
+ * Note, this callback triggers regardless of registered subscription.
+ *
+ * @param capability the new phone capability
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
+ }
+
+ /**
+ * Interface for active data subscription ID listener.
+ */
+ public interface ActiveDataSubscriptionIdChangedListener {
+ /**
+ * Callback invoked when active data subscription ID changes.
+ * Note, this callback triggers regardless of registered subscription.
+ *
+ * @param subId current subscription used to setup Cellular Internet data.
+ * For example, it could be the current active opportunistic subscription
+ * in use, or the subscription user selected as default data subscription in
+ * DSDS mode.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void onActiveDataSubscriptionIdChanged(int subId);
+ }
+
+ /**
+ * Interface for modem radio power state listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface RadioPowerStateChangedListener {
+ /**
+ * Callback invoked when modem radio power state changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state the modem radio power state
+ */
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void onRadioPowerStateChanged(@RadioPowerState int state);
+ }
+
+ /**
+ * Interface for carrier network listener.
+ */
+ public interface CarrierNetworkChangeListener {
+ /**
+ * Callback invoked when telephony has received notice from a carrier
+ * app that a network action that could result in connectivity loss
+ * has been requested by an app using
+ * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)}
+ *
+ * This is optional and is only used to allow the system to provide alternative UI while
+ * telephony is performing an action that may result in intentional, temporary network
+ * lack of connectivity.
+ *
+ * Note, this callback is pinned to the registered subscription and will be invoked when
+ * the notifying carrier app has carrier privilege rule on the registered
+ * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
+ *
+ * @param active If the carrier network change is or shortly will be active,
+ * {@code true} indicate that showing alternative UI, {@code false} otherwise.
+ */
+ public void onCarrierNetworkChange(boolean active);
+ }
+
+ /**
+ * Interface for registration failures listener.
+ */
+ public interface RegistrationFailedListener {
+ /**
+ * Report that Registration or a Location/Routing/Tracking Area update has failed.
+ *
+ * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+ * area update fails. This includes procedures that do not necessarily result in a change of
+ * the modem's registration status. If the modem's registration status changes, that is
+ * reflected in the onNetworkStateChanged() and subsequent
+ * get{Voice/Data}RegistrationState().
+ *
+ * <p>Because registration failures are ephemeral, this callback is not sticky.
+ * Registrants will not receive the most recent past value when registering.
+ *
+ * @param cellIdentity the CellIdentity, which must include the globally unique identifier
+ * for the cell (for example, all components of the CGI or ECGI).
+ * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+ * cell that was chosen for the failed registration attempt.
+ * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+ * @param causeCode the primary failure cause code of the procedure.
+ * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+ * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+ * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+ * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+ * Integer.MAX_VALUE if this value is unused.
+ * @param additionalCauseCode the cause code of any secondary/combined procedure
+ * if appropriate. For UMTS, if a combined attach succeeds for
+ * PS only, then the GMM cause code shall be included as an
+ * additionalCauseCode. For LTE (ESM), cause codes are in
+ * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+ */
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+ @NonNull String chosenPlmn, @Domain int domain,
+ int causeCode, int additionalCauseCode);
+ }
+
+ /**
+ * Interface for call attributes listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface CallAttributesChangedListener {
+ /**
+ * Callback invoked when the call attributes changes on the registered subscription.
+ * Note, the registration subscription ID comes from {@link TelephonyManager} object
+ * which registers PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subscription ID. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param callAttributes the call attributes
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ void onCallAttributesChanged(@NonNull CallAttributes callAttributes);
+ }
+
+ /**
+ * Interface for barring information listener.
+ */
+ public interface BarringInfoChangedListener {
+ /**
+ * Report updated barring information for the current camped/registered cell.
+ *
+ * <p>Barring info is provided for all services applicable to the current camped/registered
+ * cell, for the registered PLMN and current access class/access category.
+ *
+ * @param barringInfo for all services on the current cell.
+ * @see android.telephony.BarringInfo
+ */
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
+ public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
+ }
+
+ /**
+ * Interface for current physical channel configuration listener.
+ * @hide
+ */
+ @SystemApi
+ public interface PhysicalChannelConfigChangedListener {
+ /**
+ * Callback invoked when the current physical channel configuration has changed
+ *
+ * @param configs List of the current {@link PhysicalChannelConfig}s
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
+ }
+
+ /**
+ * Interface for data enabled listener.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface DataEnabledChangedListener {
+ /**
+ * Callback invoked when the data enabled changes.
+ *
+ * @param enabled {@code true} if data is enabled, otherwise disabled.
+ * @param reason Reason for data enabled/disabled.
+ * See {@link TelephonyManager.DataEnabledReason}.
+ */
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onDataEnabledChanged(boolean enabled,
+ @DataEnabledReason int reason);
}
/**
@@ -707,6 +2004,7 @@ public class PhoneStateListener {
* same as above, but with the network type. Both called.
*/
public void onDataConnectionStateChanged(int state, int networkType) {
+ // default implementation empty
}
/**
@@ -754,6 +2052,7 @@ public class PhoneStateListener {
* @param cellInfo is the list of currently visible cells.
*/
public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ // default implementation empty
}
/**
@@ -767,7 +2066,7 @@ public class PhoneStateListener {
* @param callState {@link PreciseCallState}
* @hide
*/
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@SystemApi
public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
// default implementation empty
@@ -786,9 +2085,9 @@ public class PhoneStateListener {
* @param preciseDisconnectCause {@link PreciseDisconnectCause}.
*
*/
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
- int preciseDisconnectCause) {
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause,
+ @PreciseDisconnectCauses int preciseDisconnectCause) {
// default implementation empty
}
@@ -804,7 +2103,7 @@ public class PhoneStateListener {
* @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
*
*/
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
// default implementation empty
}
@@ -826,7 +2125,7 @@ public class PhoneStateListener {
*
* @param dataConnectionState {@link PreciseDataConnectionState}
*/
- @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void onPreciseDataConnectionStateChanged(
@NonNull PreciseDataConnectionState dataConnectionState) {
// default implementation empty
@@ -864,6 +2163,7 @@ public class PhoneStateListener {
*/
@SystemApi
public void onSrvccStateChanged(@SrvccState int srvccState) {
+ // default implementation empty
}
@@ -882,6 +2182,7 @@ public class PhoneStateListener {
*/
@SystemApi
public void onVoiceActivationStateChanged(@SimActivationState int state) {
+ // default implementation empty
}
/**
@@ -898,6 +2199,7 @@ public class PhoneStateListener {
* @hide
*/
public void onDataActivationStateChanged(@SimActivationState int state) {
+ // default implementation empty
}
/**
@@ -925,7 +2227,7 @@ public class PhoneStateListener {
*
* @param telephonyDisplayInfo The display information.
*/
- @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE))
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
// default implementation empty
}
@@ -1036,7 +2338,8 @@ public class PhoneStateListener {
/**
* Callback invoked when OEM hook raw event is received on the registered subscription.
* Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * PhoneStateListener by
+ * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}.
* If this TelephonyManager object was created with
* {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
* subId. Otherwise, this callback applies to
@@ -1058,7 +2361,7 @@ public class PhoneStateListener {
* @param capability the new phone capability
* @hide
*/
- public void onPhoneCapabilityChanged(PhoneCapability capability) {
+ public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) {
// default implementation empty
}
@@ -1102,7 +2405,8 @@ public class PhoneStateListener {
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
- * @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}
+ * Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+ *
* @param state the modem radio power state
* @hide
*/
@@ -1178,18 +2482,6 @@ public class PhoneStateListener {
}
/**
- * Callback invoked when the current physical channel configuration has changed
- *
- * @param configs List of the current {@link PhysicalChannelConfig}s
- * @hide
- */
- @SystemApi
- public void onPhysicalChannelConfigurationChanged(
- @NonNull List<PhysicalChannelConfig> configs) {
- // default implementation empty
- }
-
- /**
* The callback methods need to be called on the handler thread where
* this object was created. If the binder did that for us it'd be nice.
*
@@ -1471,18 +2763,16 @@ public class PhoneStateListener {
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(
() -> psl.onImsCallDisconnectCauseChanged(disconnectCause)));
-
}
public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
- @NonNull String chosenPlmn, int domain,
- int causeCode, int additionalCauseCode) {
+ @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(() -> psl.onRegistrationFailed(
- cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+ cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
// default implementation empty
}
@@ -1494,16 +2784,29 @@ public class PhoneStateListener {
() -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo)));
}
- public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+ public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
+ PhysicalChannelConfigChangedListener listener =
+ (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get();
+ if (listener == null) return;
Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onPhysicalChannelConfigurationChanged(configs)));
+ () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged(
+ configs)));
}
- }
+ public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) {
+ if ((mPhoneStateListenerWeakRef.get() instanceof DataEnabledChangedListener)) {
+ DataEnabledChangedListener listener =
+ (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get();
+ if (listener == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> listener.onDataEnabledChanged(
+ enabled, reason)));
+ }
+ }
+ }
private void log(String s) {
Rlog.d(LOG_TAG, s);
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 24ed29a66654..cc3284a1f885 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -15,6 +15,7 @@
*/
package android.telephony;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -24,6 +25,9 @@ import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.Annotation.CallState;
@@ -37,6 +41,7 @@ import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -45,6 +50,7 @@ import com.android.internal.telephony.ITelephonyRegistry;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -206,7 +212,7 @@ public class TelephonyRegistryManager {
}
/**
- * To check the SDK version for {@link #listenForSubscriber}.
+ * To check the SDK version for {@link #listenWithEventList}.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
@@ -218,23 +224,23 @@ public class TelephonyRegistryManager {
* @param pkg Package name
* @param featureId Feature ID
* @param listener Listener providing callback
- * @param events Events
+ * @param events List events
* @param notifyNow Whether to notify instantly
*/
- public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId,
- @NonNull PhoneStateListener listener, long events, boolean notifyNow) {
+ public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId,
+ @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) {
try {
// subId from PhoneStateListener is deprecated Q on forward, use the subId from
// TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
// Since mSubId in PhoneStateListener is deprecated from Q on forward, this is
// the only place to set mSubId and its for "informational" only.
- listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
+ listener.mSubId = (events.length == 0)
? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
} else if (listener.mSubId != null) {
subId = listener.mSubId;
}
- sRegistry.listenForSubscriber(
+ sRegistry.listenWithEventList(
subId, pkg, featureId, listener.callback, events, notifyNow);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -771,13 +777,345 @@ public class TelephonyRegistryManager {
* @param subId the subId
* @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
*/
- public void notifyPhysicalChannelConfigurationForSubscriber(
+ public void notifyPhysicalChannelConfigForSubscriber(
int subId, List<PhysicalChannelConfig> configs) {
try {
- sRegistry.notifyPhysicalChannelConfigurationForSubscriber(subId, configs);
+ sRegistry.notifyPhysicalChannelConfigForSubscriber(subId, configs);
} catch (RemoteException ex) {
// system server crash
}
}
+ public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) {
+
+ Set<Integer> eventList = new ArraySet<>();
+
+ if (listener instanceof PhoneStateListener.ServiceStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.CellLocationChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.CallStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.DataActivityListener) {
+ eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.CellInfoChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.SrvccStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) {
+ eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
+ }
+
+ if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) {
+ eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+ }
+
+ if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) {
+ eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.RegistrationFailedListener) {
+ eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
+ }
+
+ if (listener instanceof PhoneStateListener.CallAttributesChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.BarringInfoChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ }
+
+ if (listener instanceof PhoneStateListener.DataEnabledChangedListener) {
+ eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
+ }
+
+ return eventList;
+ }
+
+ private @NonNull Set<Integer> getEventsFromBitmask(int eventMask) {
+
+ Set<Integer> eventList = new ArraySet<>();
+
+ if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
+ eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
+ eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
+ eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
+ eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
+ eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
+ eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
+ eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) {
+ eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) {
+ eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
+ eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) {
+ eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+ eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
+ eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
+ eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
+ eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
+ eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
+ eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) {
+ eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) {
+ eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) {
+ eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
+ }
+
+ if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
+ eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ }
+ return eventList;
+
+ }
+
+ /**
+ * Registers a listener object to receive notification of changes
+ * in specified telephony states.
+ * <p>
+ * To register a listener, pass a {@link PhoneStateListener} which implements
+ * interfaces of events. For example,
+ * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
+ * {@link PhoneStateListener.ServiceStateChangedListener}.
+ *
+ * At registration, and when a specified telephony state changes, the telephony manager invokes
+ * the appropriate callback method on the listener object and passes the current (updated)
+ * values.
+ * <p>
+ *
+ * If this TelephonyManager object has been created with
+ * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId.
+ * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ * To listen events for multiple subIds, pass a separate listener object to
+ * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}.
+ *
+ * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+ * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+ * {@link SecurityException} will be thrown otherwise.
+ *
+ * This API should be used sparingly -- large numbers of listeners will cause system
+ * instability. If a process has registered too many listeners without unregistering them, it
+ * may encounter an {@link IllegalStateException} when trying to register more listeners.
+ *
+ * @param listener The {@link PhoneStateListener} object to register.
+ */
+ public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId,
+ String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
+ boolean notifyNow) {
+ listener.setExecutor(executor);
+ registerPhoneStateListener(subId, pkgName, attributionTag, listener,
+ getEventsFromListener(listener), notifyNow);
+ }
+
+ public void registerPhoneStateListenerWithEvents(int subId, String pkgName,
+ String attributionTag, @NonNull PhoneStateListener listener, int events,
+ boolean notifyNow) {
+ registerPhoneStateListener(
+ subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow);
+ }
+
+ private void registerPhoneStateListener(int subId,
+ String pkgName, String attributionTag, @NonNull PhoneStateListener listener,
+ @NonNull Set<Integer> events, boolean notifyNow) {
+ if (listener == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+
+ listenWithEventList(subId, pkgName, attributionTag, listener,
+ events.stream().mapToInt(i -> i).toArray(), notifyNow);
+ }
+
+ /**
+ * Unregister an existing {@link PhoneStateListener}.
+ *
+ * @param listener The {@link PhoneStateListener} object to unregister.
+ */
+ public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag,
+ @NonNull PhoneStateListener listener,
+ boolean notifyNow) {
+ listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow);
+ }
}
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 84c209b2d063..00ff68736182 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -698,7 +698,7 @@ public class DateFormat {
}
private static String zeroPad(int inValue, int inMinDigits) {
- return TextUtils.formatSimple("%0" + inMinDigits + "d", inValue);
+ return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue);
}
/**
diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java
index cc6ed2e4539e..c8193b3b0dbb 100644
--- a/core/java/android/util/Xml.java
+++ b/core/java/android/util/Xml.java
@@ -18,6 +18,8 @@ package android.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.system.ErrnoException;
+import android.system.Os;
import com.android.internal.util.BinaryXmlPullParser;
import com.android.internal.util.BinaryXmlSerializer;
@@ -35,7 +37,7 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -43,6 +45,7 @@ import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
/**
* XML utility methods.
@@ -59,6 +62,12 @@ public class Xml {
public static String FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed";
/**
+ * Feature flag: when set, {@link #resolveSerializer(OutputStream)} will
+ * emit binary XML by default.
+ */
+ private static final boolean ENABLE_BINARY_DEFAULT = false;
+
+ /**
* Parses the given xml string and fires events on the given SAX handler.
*/
public static void parse(String xml, ContentHandler contentHandler)
@@ -119,6 +128,7 @@ public class Xml {
*
* @hide
*/
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static @NonNull TypedXmlPullParser newFastPullParser() {
return XmlUtils.makeTyped(newPullParser());
}
@@ -151,8 +161,28 @@ public class Xml {
*/
public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
throws IOException {
- // TODO: add support for binary format
- final TypedXmlPullParser xml = newFastPullParser();
+ final byte[] magic = new byte[4];
+ if (in instanceof FileInputStream) {
+ try {
+ Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ } else {
+ if (!in.markSupported()) {
+ in = new BufferedInputStream(in);
+ }
+ in.mark(4);
+ in.read(magic);
+ in.reset();
+ }
+
+ final TypedXmlPullParser xml;
+ if (Arrays.equals(magic, BinaryXmlSerializer.PROTOCOL_MAGIC_VERSION_0)) {
+ xml = newBinaryPullParser();
+ } else {
+ xml = newFastPullParser();
+ }
try {
xml.setInput(in, StandardCharsets.UTF_8.name());
} catch (XmlPullParserException e) {
@@ -177,6 +207,7 @@ public class Xml {
*
* @hide
*/
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static @NonNull TypedXmlSerializer newFastSerializer() {
return XmlUtils.makeTyped(new FastXmlSerializer());
}
@@ -209,8 +240,12 @@ public class Xml {
*/
public static @NonNull TypedXmlSerializer resolveSerializer(@NonNull OutputStream out)
throws IOException {
- // TODO: add support for binary format
- final TypedXmlSerializer xml = newFastSerializer();
+ final TypedXmlSerializer xml;
+ if (ENABLE_BINARY_DEFAULT) {
+ xml = newBinarySerializer();
+ } else {
+ xml = newFastSerializer();
+ }
xml.setOutput(out, StandardCharsets.UTF_8.name());
return xml;
}
diff --git a/core/java/android/uwb/AdapterStateListener.java b/core/java/android/uwb/AdapterStateListener.java
new file mode 100644
index 000000000000..8875af385238
--- /dev/null
+++ b/core/java/android/uwb/AdapterStateListener.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.uwb.UwbManager.AdapterStateCallback;
+import android.uwb.UwbManager.AdapterStateCallback.StateChangedReason;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
+ private static final String TAG = "Uwb.StateListener";
+
+ private final IUwbAdapter mAdapter;
+ private boolean mIsRegistered = false;
+
+ private final Map<AdapterStateCallback, Executor> mCallbackMap = new HashMap<>();
+
+ @StateChangedReason
+ private int mAdapterStateChangeReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
+ private boolean mAdapterEnabledState = false;
+
+ public AdapterStateListener(@NonNull IUwbAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /**
+ * Register an {@link AdapterStateCallback} with this {@link AdapterStateListener}
+ *
+ * @param executor an {@link Executor} to execute given callback
+ * @param callback user implementation of the {@link AdapterStateCallback}
+ */
+ public void register(@NonNull Executor executor, @NonNull AdapterStateCallback callback) {
+ synchronized (this) {
+ if (mCallbackMap.containsKey(callback)) {
+ return;
+ }
+
+ mCallbackMap.put(callback, executor);
+
+ if (!mIsRegistered) {
+ try {
+ mAdapter.registerAdapterStateCallbacks(this);
+ mIsRegistered = true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to register adapter state callback");
+ executor.execute(() -> callback.onStateChanged(false,
+ AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN));
+ }
+ } else {
+ sendCurrentState(callback);
+ }
+ }
+ }
+
+ /**
+ * Unregister the specified {@link AdapterStateCallback}
+ *
+ * @param callback user implementation of the {@link AdapterStateCallback}
+ */
+ public void unregister(@NonNull AdapterStateCallback callback) {
+ synchronized (this) {
+ if (!mCallbackMap.containsKey(callback)) {
+ return;
+ }
+
+ mCallbackMap.remove(callback);
+
+ if (mCallbackMap.isEmpty() && mIsRegistered) {
+ try {
+ mAdapter.unregisterAdapterStateCallbacks(this);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to unregister AdapterStateCallback with service");
+ }
+ mIsRegistered = false;
+ }
+ }
+ }
+
+ private void sendCurrentState(@NonNull AdapterStateCallback callback) {
+ synchronized (this) {
+ Executor executor = mCallbackMap.get(callback);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onStateChanged(
+ mAdapterEnabledState, mAdapterStateChangeReason));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onAdapterStateChanged(boolean isEnabled, int reason) {
+ synchronized (this) {
+ @StateChangedReason int localReason =
+ convertToStateChangedReason(reason);
+ mAdapterEnabledState = isEnabled;
+ mAdapterStateChangeReason = localReason;
+ for (AdapterStateCallback cb : mCallbackMap.keySet()) {
+ sendCurrentState(cb);
+ }
+ }
+ }
+
+ private static @StateChangedReason int convertToStateChangedReason(
+ @StateChangeReason int reason) {
+ switch (reason) {
+ case StateChangeReason.ALL_SESSIONS_CLOSED:
+ return AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED;
+
+ case StateChangeReason.SESSION_STARTED:
+ return AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED;
+
+ case StateChangeReason.SYSTEM_POLICY:
+ return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY;
+
+ case StateChangeReason.SYSTEM_BOOT:
+ return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT;
+
+ case StateChangeReason.UNKNOWN:
+ default:
+ return AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
+ }
+ }
+}
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index d29ed34804f1..2c8b2e462510 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -98,11 +98,18 @@ interface IUwbAdapter {
int getMaxSimultaneousSessions();
/**
- * Get the maximum number of remote devices per session
+ * Get the maximum number of remote devices per session when local device is initiator
*
* @return the maximum number of remote devices supported in a single session
*/
- int getMaxRemoteDevicesPerSession();
+ int getMaxRemoteDevicesPerInitiatorSession();
+
+ /**
+ * Get the maximum number of remote devices per session when local device is responder
+ *
+ * @return the maximum number of remote devices supported in a single session
+ */
+ int getMaxRemoteDevicesPerResponderSession();
/**
* Provides the capabilities and features of the device
diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl
index 46a6e2edfa22..28eaf9f2b24e 100644
--- a/core/java/android/uwb/StateChangeReason.aidl
+++ b/core/java/android/uwb/StateChangeReason.aidl
@@ -41,5 +41,10 @@ enum StateChangeReason {
* The adapter state changed because of a device system change.
*/
SYSTEM_POLICY,
+
+ /**
+ * Used to signal the first adapter state message after boot
+ */
+ SYSTEM_BOOT,
}
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 2f1e2ded26ac..ed5cf3625525 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -16,13 +16,21 @@
package android.uwb;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.IBinder;
import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -36,7 +44,13 @@ import java.util.concurrent.Executor;
*
* @hide
*/
+@SystemService(Context.UWB_SERVICE)
public final class UwbManager {
+ private IUwbAdapter mUwbAdapter;
+ private static final String SERVICE_NAME = "uwb";
+
+ private AdapterStateListener mAdapterStateListener;
+
/**
* Interface for receiving UWB adapter state changes
*/
@@ -96,10 +110,31 @@ public final class UwbManager {
/**
* Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance.
+ *
+ * @param adapter an instance of an {@link android.uwb.IUwbAdapter}
*/
- private UwbManager() {
- throw new UnsupportedOperationException();
+ private UwbManager(IUwbAdapter adapter) {
+ mUwbAdapter = adapter;
+ mAdapterStateListener = new AdapterStateListener(adapter);
}
+
+ /**
+ * @hide
+ */
+ public static UwbManager getInstance() {
+ IBinder b = ServiceManager.getService(SERVICE_NAME);
+ if (b == null) {
+ return null;
+ }
+
+ IUwbAdapter adapter = IUwbAdapter.Stub.asInterface(b);
+ if (adapter == null) {
+ return null;
+ }
+
+ return new UwbManager(adapter);
+ }
+
/**
* Register an {@link AdapterStateCallback} to listen for UWB adapter state changes
* <p>The provided callback will be invoked by the given {@link Executor}.
@@ -112,8 +147,9 @@ public final class UwbManager {
* @param executor an {@link Executor} to execute given callback
* @param callback user implementation of the {@link AdapterStateCallback}
*/
- public void registerAdapterStateCallback(Executor executor, AdapterStateCallback callback) {
- throw new UnsupportedOperationException();
+ public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AdapterStateCallback callback) {
+ mAdapterStateListener.register(executor, callback);
}
/**
@@ -125,8 +161,8 @@ public final class UwbManager {
*
* @param callback user implementation of the {@link AdapterStateCallback}
*/
- public void unregisterAdapterStateCallback(AdapterStateCallback callback) {
- throw new UnsupportedOperationException();
+ public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) {
+ mAdapterStateListener.unregister(callback);
}
/**
@@ -139,7 +175,11 @@ public final class UwbManager {
*/
@NonNull
public PersistableBundle getSpecificationInfo() {
- throw new UnsupportedOperationException();
+ try {
+ return mUwbAdapter.getSpecificationInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -148,7 +188,11 @@ public final class UwbManager {
* @return true if ranging is supported
*/
public boolean isRangingSupported() {
- throw new UnsupportedOperationException();
+ try {
+ return mUwbAdapter.isRangingSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@Retention(RetentionPolicy.SOURCE)
@@ -197,7 +241,24 @@ public final class UwbManager {
*/
@AngleOfArrivalSupportType
public int getAngleOfArrivalSupport() {
- throw new UnsupportedOperationException();
+ try {
+ switch (mUwbAdapter.getAngleOfArrivalSupport()) {
+ case AngleOfArrivalSupport.TWO_DIMENSIONAL:
+ return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D;
+
+ case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL:
+ return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL;
+
+ case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL:
+ return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL;
+
+ case AngleOfArrivalSupport.NONE:
+ default:
+ return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -211,7 +272,15 @@ public final class UwbManager {
*/
@NonNull
public List<Integer> getSupportedChannelNumbers() {
- throw new UnsupportedOperationException();
+ List<Integer> channels = new ArrayList<>();
+ try {
+ for (int channel : mUwbAdapter.getSupportedChannels()) {
+ channels.add(channel);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return channels;
}
/**
@@ -222,7 +291,15 @@ public final class UwbManager {
*/
@NonNull
public Set<Integer> getSupportedPreambleCodeIndices() {
- throw new UnsupportedOperationException();
+ Set<Integer> preambles = new HashSet<>();
+ try {
+ for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) {
+ preambles.add(preamble);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return preambles;
}
/**
@@ -234,7 +311,11 @@ public final class UwbManager {
*/
@SuppressLint("MethodNameUnits")
public long elapsedRealtimeResolutionNanos() {
- throw new UnsupportedOperationException();
+ try {
+ return mUwbAdapter.getTimestampResolutionNanos();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -243,7 +324,11 @@ public final class UwbManager {
* @return the maximum allowed number of simultaneously open {@link RangingSession} instances.
*/
public int getMaxSimultaneousSessions() {
- throw new UnsupportedOperationException();
+ try {
+ return mUwbAdapter.getMaxSimultaneousSessions();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -253,7 +338,11 @@ public final class UwbManager {
* @return the maximum number of remote devices per {@link RangingSession}
*/
public int getMaxRemoteDevicesPerInitiatorSession() {
- throw new UnsupportedOperationException();
+ try {
+ return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -263,7 +352,11 @@ public final class UwbManager {
* @return the maximum number of remote devices per {@link RangingSession}
*/
public int getMaxRemoteDevicesPerResponderSession() {
- throw new UnsupportedOperationException();
+ try {
+ return mUwbAdapter.getMaxRemoteDevicesPerResponderSession();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
index b58937beed55..fcf699faafe1 100644
--- a/core/java/android/view/ContentInfo.java
+++ b/core/java/android/view/ContentInfo.java
@@ -20,16 +20,16 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipData;
+import android.content.ClipDescription;
import android.net.Uri;
import android.os.Bundle;
-import android.util.ArrayMap;
+import android.util.Pair;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
@@ -208,50 +208,51 @@ public final class ContentInfo {
}
/**
- * Partitions the content based on the given predicate.
+ * Partitions this content based on the given predicate.
*
- * <p>Similar to a
- * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector},
- * this function classifies the content and organizes it into a map, grouping the items that
- * matched vs didn't match the predicate.
+ * <p>This function classifies the content and organizes it into a pair, grouping the items
+ * that matched vs didn't match the predicate.
*
* <p>Except for the {@link ClipData} items, the returned objects will contain all the same
- * metadata as the original.
+ * metadata as this {@link ContentInfo}.
*
* @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which
- * partition to place it into.
- * @return A map containing the partitioned content. The map will contain a single entry if
- * all items were classified into the same partition (all matched or all didn't match the
- * predicate) or two entries (if there's at least one item that matched the predicate and at
- * least one item that didn't match the predicate).
+ * partition to place it into.
+ * @return A pair containing the partitioned content. The pair's first object will have the
+ * content that matched the predicate, or null if none of the items matched. The pair's
+ * second object will have the content that didn't match the predicate, or null if all of
+ * the items matched.
*/
@NonNull
- public Map<Boolean, ContentInfo> partition(@NonNull Predicate<ClipData.Item> itemPredicate) {
+ public Pair<ContentInfo, ContentInfo> partition(
+ @NonNull Predicate<ClipData.Item> itemPredicate) {
if (mClip.getItemCount() == 1) {
- Map<Boolean, ContentInfo> result = new ArrayMap<>(1);
- result.put(itemPredicate.test(mClip.getItemAt(0)), this);
- return result;
+ boolean matched = itemPredicate.test(mClip.getItemAt(0));
+ return Pair.create(matched ? this : null, matched ? null : this);
}
- ArrayList<ClipData.Item> accepted = new ArrayList<>();
- ArrayList<ClipData.Item> remaining = new ArrayList<>();
+ ArrayList<ClipData.Item> acceptedItems = new ArrayList<>();
+ ArrayList<ClipData.Item> remainingItems = new ArrayList<>();
for (int i = 0; i < mClip.getItemCount(); i++) {
ClipData.Item item = mClip.getItemAt(i);
if (itemPredicate.test(item)) {
- accepted.add(item);
+ acceptedItems.add(item);
} else {
- remaining.add(item);
+ remainingItems.add(item);
}
}
- Map<Boolean, ContentInfo> result = new ArrayMap<>(2);
- if (!accepted.isEmpty()) {
- ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted);
- result.put(true, new Builder(this).setClip(acceptedClip).build());
+ if (acceptedItems.isEmpty()) {
+ return Pair.create(null, this);
}
- if (!remaining.isEmpty()) {
- ClipData remainingClip = new ClipData(mClip.getDescription(), remaining);
- result.put(false, new Builder(this).setClip(remainingClip).build());
+ if (remainingItems.isEmpty()) {
+ return Pair.create(this, null);
}
- return result;
+ ContentInfo accepted = new Builder(this)
+ .setClip(new ClipData(new ClipDescription(mClip.getDescription()), acceptedItems))
+ .build();
+ ContentInfo remaining = new Builder(this)
+ .setClip(new ClipData(new ClipDescription(mClip.getDescription()), remainingItems))
+ .build();
+ return Pair.create(accepted, remaining);
}
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 56c7e27151e0..9991367e6bfd 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1512,6 +1512,9 @@ public final class Display {
* then 1920x1080 50Hz will have alternative refresh rate of 60Hz. If 1920x1080 60Hz
* has an alternative of 50Hz and 1920x1080 50Hz has an alternative of 24Hz, then 1920x1080
* 60Hz will also have an alternative of 24Hz.
+ *
+ * @see Surface#setFrameRate
+ * @see SurfaceControl.Transaction#setFrameRate
*/
@NonNull
public float[] getAlternativeRefreshRates() {
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
deleted file mode 100644
index 1cf83a3f1e79..000000000000
--- a/core/java/android/view/IPinnedStackController.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * 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 android.view;
-
-import android.graphics.Rect;
-
-/**
- * An interface to the PinnedStackController to update it of state changes, and to query
- * information based on the current state.
- *
- * @hide
- */
-interface IPinnedStackController {
- /**
- * @return what WM considers to be the current device rotation.
- */
- int getDisplayRotation();
-}
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index f49fee36ca7e..84dd8af5e342 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -21,7 +21,6 @@ import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
import android.view.DisplayInfo;
-import android.view.IPinnedStackController;
/**
* Listener for changes to the pinned stack made by the WindowManager.
@@ -31,12 +30,6 @@ import android.view.IPinnedStackController;
oneway interface IPinnedStackListener {
/**
- * Called when the listener is registered and provides an interface to call back to the pinned
- * stack controller to update the controller of the pinned stack state.
- */
- void onListenerRegistered(IPinnedStackController controller);
-
- /**
* Called when the window manager has detected a change that would cause the movement bounds
* to be changed (ie. after configuration change, aspect ratio change, etc).
*/
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a23b7e193024..025a977d5420 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -780,4 +780,25 @@ interface IWindowManager
* verified.
*/
boolean verifyImpressionToken(in ImpressionToken impressionToken);
+
+ /**
+ * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
+ * from the server side.
+ *
+ * @param clientToken the window context's token
+ * @param type Window type of the window context
+ * @param displayId The display associated with the window context
+ * @param options A bundle used to pass window-related options and choose the right DisplayArea
+ *
+ * @return {@code true} if the listener was registered successfully.
+ */
+ boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
+ in Bundle options);
+
+ /**
+ * Unregisters a listener which registered with {@link #registerWindowContextListener()}.
+ *
+ * @param clientToken the window context's token
+ */
+ void unregisterWindowContextListener(IBinder clientToken);
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c4f32c433598..80437be1dcd4 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1057,6 +1057,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean canRun = false;
if (show) {
// Show request
+ if (fromIme) {
+ ImeTracing.getInstance().triggerClientDump(
+ "ImeInsetsSourceConsumer#requestShow", mHost.getInputMethodManager(),
+ null /* icProto */);
+ }
switch(consumer.requestShow(fromIme)) {
case ShowResult.SHOW_IMMEDIATELY:
canRun = true;
@@ -1096,8 +1101,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
+ fromIme);
// We don't have a control at the moment. However, we still want to update requested
// visibility state such that in case we get control, we can apply show animation.
+ if (fromIme) {
+ ImeTracing.getInstance().triggerClientDump(
+ "InsetsSourceConsumer#show", mHost.getInputMethodManager(),
+ null /* icProto */);
+ }
consumer.show(fromIme);
} else if (animationType == ANIMATION_TYPE_HIDE) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerClientDump(
+ "InsetsSourceConsumer#hide", mHost.getInputMethodManager(),
+ null /* icProto */);
+ }
consumer.hide();
}
}
@@ -1217,8 +1232,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void applyLocalVisibilityOverride() {
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
- final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
- controller.applyLocalVisibilityOverride();
+ final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ consumer.applyLocalVisibilityOverride();
}
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 537fd42d7135..fd1c3b82ca2c 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -25,6 +25,7 @@ import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;
@@ -34,6 +35,7 @@ import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.util.Log;
+import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
@@ -108,6 +110,10 @@ public class InsetsSourceConsumer {
*/
public void setControl(@Nullable InsetsSourceControl control,
@InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
+ if (mType == ITYPE_IME) {
+ ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl",
+ mController.getHost().getInputMethodManager(), null /* icProto */);
+ }
if (mSourceControl == control) {
return;
}
@@ -237,6 +243,12 @@ public class InsetsSourceConsumer {
final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
final boolean hasControl = mSourceControl != null;
+ if (mType == ITYPE_IME) {
+ ImeTracing.getInstance().triggerClientDump(
+ "InsetsSourceConsumer#applyLocalVisibilityOverride",
+ mController.getHost().getInputMethodManager(), null /* icProto */);
+ }
+
// We still need to let the legacy app know the visibility change even if we don't have the
// control. If we don't have the source, we don't change the requested visibility for making
// the callback behavior compatible.
diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java
index b551fa80b5cd..419f964ccd25 100644
--- a/core/java/android/view/OnReceiveContentListener.java
+++ b/core/java/android/view/OnReceiveContentListener.java
@@ -35,12 +35,12 @@ import android.annotation.Nullable;
*
* &#64;Override
* public ContentInfo onReceiveContent(View view, ContentInfo payload) {
- * Map&lt;Boolean, ContentInfo&gt; split =
+ * Pair&lt;ContentInfo, ContentInfo&gt; split =
* payload.partition(item -&gt; item.getUri() != null);
- * ContentInfo uriItems = split.get(true);
- * ContentInfo remainingItems = split.get(false);
- * if (uriItems != null) {
- * ClipData clip = uriItems.getClip();
+ * ContentInfo uriContent = split.first;
+ * ContentInfo remaining = split.second;
+ * if (uriContent != null) {
+ * ClipData clip = uriContent.getClip();
* for (int i = 0; i < clip.getItemCount(); i++) {
* Uri uri = clip.getItemAt(i).getUri();
* // ... app-specific logic to handle the URI ...
@@ -48,7 +48,7 @@ import android.annotation.Nullable;
* }
* // Return anything that we didn't handle ourselves. This preserves the default platform
* // behavior for text and anything else for which we are not implementing custom handling.
- * return remainingItems;
+ * return remaining;
* }
* }
*
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 57ca71ae4b02..d839e3532b64 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.FrameInfo;
import android.graphics.HardwareRenderer;
import android.graphics.Picture;
import android.graphics.Point;
@@ -610,8 +611,7 @@ public final class ThreadedRenderer extends HardwareRenderer {
* @param attachInfo AttachInfo tied to the specified view.
*/
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
- final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
- choreographer.mFrameInfo.markDrawStart();
+ attachInfo.mViewRootImpl.mViewFrameInfo.markDrawStart();
updateRootDisplayList(view, callbacks);
@@ -629,7 +629,9 @@ public final class ThreadedRenderer extends HardwareRenderer {
attachInfo.mPendingAnimatingRenderNodes = null;
}
- int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
+ final FrameInfo frameInfo = attachInfo.mViewRootImpl.getUpdatedFrameInfo();
+
+ int syncResult = syncAndDrawFrame(frameInfo);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
Log.w("OpenGLRenderer", "Surface lost, forcing relayout");
// We lost our surface. For a relayout next frame which should give us a new
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a5c66537b11f..29cc4b507acc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -27953,10 +27953,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* Requests pointer capture mode.
* <p>
* When the window has pointer capture, the mouse pointer icon will disappear and will not
- * change its position. Further mouse will be dispatched with the source
- * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be available
- * through {@link MotionEvent#getX} and {@link MotionEvent#getY}. Non-mouse events
- * (touchscreens, or stylus) will not be affected.
+ * change its position. Enabling pointer capture will change the behavior of input devices in
+ * the following ways:
+ * <ul>
+ * <li>Events from a mouse will be delivered with the source
+ * {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be
+ * available through {@link MotionEvent#getX} and {@link MotionEvent#getY}.</li>
+ *
+ * <li>Events from a touchpad will be delivered with the source
+ * {@link InputDevice#SOURCE_TOUCHPAD}, where the absolute position of each of the pointers
+ * on the touchpad will be available through {@link MotionEvent#getX(int)} and
+ * {@link MotionEvent#getY(int)}, and their relative movements are stored in
+ * {@link MotionEvent#AXIS_RELATIVE_X} and {@link MotionEvent#AXIS_RELATIVE_Y}.</li>
+ *
+ * <li>Events from other types of devices, such as touchscreens, will not be affected.</li>
+ * </ul>
+ * <p>
+ * Events captured through pointer capture will be dispatched to
+ * {@link OnCapturedPointerListener#onCapturedPointer(View, MotionEvent)} if an
+ * {@link OnCapturedPointerListener} is set, and otherwise to
+ * {@link #onCapturedPointerEvent(MotionEvent)}.
* <p>
* If the window already has pointer capture, this call does nothing.
* <p>
@@ -27965,6 +27981,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #releasePointerCapture()
* @see #hasPointerCapture()
+ * @see #onPointerCaptureChange(boolean)
*/
public void requestPointerCapture() {
final ViewRootImpl viewRootImpl = getViewRootImpl();
@@ -27980,6 +27997,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* If the window does not have pointer capture, this call will do nothing.
* @see #requestPointerCapture()
* @see #hasPointerCapture()
+ * @see #onPointerCaptureChange(boolean)
*/
public void releasePointerCapture() {
final ViewRootImpl viewRootImpl = getViewRootImpl();
diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java
new file mode 100644
index 000000000000..890d071f8090
--- /dev/null
+++ b/core/java/android/view/ViewFrameInfo.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.FrameInfo;
+
+/**
+ * The timing information of events taking place in ViewRootImpl
+ * @hide
+ */
+public class ViewFrameInfo {
+ public long drawStart;
+ public long oldestInputEventTime; // the time of the oldest input event consumed for this frame
+ public long newestInputEventTime; // the time of the newest input event consumed for this frame
+ // Various flags set to provide extra metadata about the current frame. See flag definitions
+ // inside FrameInfo.
+ // @see android.graphics.FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
+ public long flags;
+
+ /**
+ * Update the oldest event time.
+ * @param eventTime the time of the input event
+ */
+ public void updateOldestInputEvent(long eventTime) {
+ if (oldestInputEventTime == 0 || eventTime < oldestInputEventTime) {
+ oldestInputEventTime = eventTime;
+ }
+ }
+
+ /**
+ * Update the newest event time.
+ * @param eventTime the time of the input event
+ */
+ public void updateNewestInputEvent(long eventTime) {
+ if (newestInputEventTime == 0 || eventTime > newestInputEventTime) {
+ newestInputEventTime = eventTime;
+ }
+ }
+
+ /**
+ * Populate the missing fields using the data from ViewFrameInfo
+ * @param frameInfo : the structure FrameInfo object to populate
+ */
+ public void populateFrameInfo(FrameInfo frameInfo) {
+ frameInfo.frameInfo[FrameInfo.FLAGS] |= flags;
+ frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart;
+ frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime;
+ frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime;
+ }
+
+ /**
+ * Reset this data. Should typically be invoked after calling "populateFrameInfo".
+ */
+ public void reset() {
+ drawStart = 0;
+ oldestInputEventTime = 0;
+ newestInputEventTime = 0;
+ flags = 0;
+ }
+
+ /**
+ * Record the current time, and store it in 'drawStart'
+ */
+ public void markDrawStart() {
+ drawStart = System.nanoTime();
+ }
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8321f2b28771..2d633cbeb353 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -19,6 +19,7 @@ package android.view;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.SIZE;
@@ -118,6 +119,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.input.InputManager;
@@ -423,7 +425,7 @@ public final class ViewRootImpl implements ViewParent,
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mHeight;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- Rect mDirty;
+ private Rect mDirty;
public boolean mIsAnimating;
private boolean mUseMTRenderer;
@@ -446,6 +448,23 @@ public final class ViewRootImpl implements ViewParent,
@UnsupportedAppUsage
FallbackEventHandler mFallbackEventHandler;
final Choreographer mChoreographer;
+ protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
+
+ /**
+ * Update the Choreographer's FrameInfo object with the timing information for the current
+ * ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
+ * frame.
+ * @return the updated FrameInfo object
+ */
+ protected @NonNull FrameInfo getUpdatedFrameInfo() {
+ // Since Choreographer is a thread-local singleton while we can have multiple
+ // ViewRootImpl's, populate the frame information from the current viewRootImpl before
+ // starting the draw
+ FrameInfo frameInfo = mChoreographer.mFrameInfo;
+ mViewFrameInfo.populateFrameInfo(frameInfo);
+ mViewFrameInfo.reset();
+ return frameInfo;
+ }
// used in relayout to get SurfaceControl size
// for BLAST adapter surface setup
@@ -2675,7 +2694,7 @@ public final class ViewRootImpl implements ViewParent,
// to resume them
mDirty.set(0, 0, mWidth, mHeight);
}
- mChoreographer.mFrameInfo.addFlags(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED);
+ mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED;
}
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
@@ -4396,6 +4415,14 @@ public final class ViewRootImpl implements ViewParent,
mView.mContext.getDrawable(value.resourceId);
}
}
+ // Sets the focus appearance data into the accessibility focus drawable.
+ if (mAttachInfo.mAccessibilityFocusDrawable instanceof GradientDrawable) {
+ final GradientDrawable drawable =
+ (GradientDrawable) mAttachInfo.mAccessibilityFocusDrawable;
+ drawable.setStroke(mAccessibilityManager.getAccessibilityFocusStrokeWidth(),
+ mAccessibilityManager.getAccessibilityFocusColor());
+ }
+
return mAttachInfo.mAccessibilityFocusDrawable;
}
@@ -7914,6 +7941,10 @@ public final class ViewRootImpl implements ViewParent,
if (mTranslator != null) {
mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
}
+ if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) {
+ ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsChanged",
+ getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
+ }
mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
}
@@ -7930,6 +7961,10 @@ public final class ViewRootImpl implements ViewParent,
if (mTranslator != null) {
mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
}
+ if (insetsState != null && insetsState.getSource(ITYPE_IME).isVisible()) {
+ ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsControlChanged",
+ getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
+ }
SomeArgs args = SomeArgs.obtain();
args.arg1 = insetsState;
args.arg2 = activeControls;
@@ -8138,7 +8173,8 @@ public final class ViewRootImpl implements ViewParent,
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
- mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
+ mViewFrameInfo.updateOldestInputEvent(oldestEventTime);
+ mViewFrameInfo.updateNewestInputEvent(eventTime);
deliverInputEvent(q);
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9c163783124e..13d9eb9d3c7d 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -45,7 +45,6 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteException;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionManager;
@@ -635,7 +634,7 @@ public abstract class Window {
* Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing
* mode and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.
*/
- void toggleFreeformWindowingMode() throws RemoteException;
+ void toggleFreeformWindowingMode();
/**
* Puts the activity in picture-in-picture mode if the activity supports.
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a8534faf43e5..56dcd5951e5e 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -19,9 +19,11 @@ package android.view.accessibility;
import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
import android.accessibilityservice.AccessibilityShortcutInfo;
+import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -59,6 +61,7 @@ import android.view.IWindow;
import android.view.View;
import android.view.accessibility.AccessibilityEvent.EventType;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IntPair;
@@ -233,6 +236,11 @@ public final class AccessibilityManager {
private int mPerformingAction = 0;
+ /** The stroke width of the focus rectangle in pixels */
+ private int mFocusStrokeWidth;
+ /** The color of the focus rectangle */
+ private int mFocusColor;
+
@UnsupportedAppUsage
private final ArrayMap<AccessibilityStateChangeListener, Handler>
mAccessibilityStateChangeListeners = new ArrayMap<>();
@@ -410,6 +418,13 @@ public final class AccessibilityManager {
public void setRelevantEventTypes(int eventTypes) {
mRelevantEventTypes = eventTypes;
}
+
+ @Override
+ public void setFocusAppearance(int strokeWidth, int color) {
+ synchronized (mLock) {
+ updateFocusAppearanceLocked(strokeWidth, color);
+ }
+ }
};
/**
@@ -457,6 +472,7 @@ public final class AccessibilityManager {
mHandler = new Handler(context.getMainLooper(), mCallback);
mUserId = userId;
synchronized (mLock) {
+ initialFocusAppearanceLocked(context.getResources());
tryConnectToServiceLocked(service);
}
}
@@ -464,18 +480,26 @@ public final class AccessibilityManager {
/**
* Create an instance.
*
+ * @param context A {@link Context}.
* @param handler The handler to use
* @param service An interface to the backing service.
* @param userId User id under which to run.
+ * @param serviceConnect {@code true} to connect the service or
+ * {@code false} not to connect the service.
*
* @hide
*/
- public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
+ @VisibleForTesting
+ public AccessibilityManager(Context context, Handler handler, IAccessibilityManager service,
+ int userId, boolean serviceConnect) {
mCallback = new MyCallback();
mHandler = handler;
mUserId = userId;
synchronized (mLock) {
- tryConnectToServiceLocked(service);
+ initialFocusAppearanceLocked(context.getResources());
+ if (serviceConnect) {
+ tryConnectToServiceLocked(service);
+ }
}
}
@@ -954,6 +978,32 @@ public final class AccessibilityManager {
}
/**
+ * Gets the strokeWidth of the focus rectangle. This value can be set by
+ * {@link AccessibilityService}.
+ *
+ * @return The strokeWidth of the focus rectangle in pixels.
+ *
+ */
+ public int getAccessibilityFocusStrokeWidth() {
+ synchronized (mLock) {
+ return mFocusStrokeWidth;
+ }
+ }
+
+ /**
+ * Gets the color of the focus rectangle. This value can be set by
+ * {@link AccessibilityService}.
+ *
+ * @return The color of the focus rectangle.
+ *
+ */
+ public @ColorInt int getAccessibilityFocusColor() {
+ synchronized (mLock) {
+ return mFocusColor;
+ }
+ }
+
+ /**
* Get the preparers that are registered for an accessibility ID
*
* @param id The ID of interest
@@ -1551,6 +1601,7 @@ public final class AccessibilityManager {
setStateLocked(IntPair.first(userStateAndRelevantEvents));
mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
updateUiTimeout(service.getRecommendedTimeoutMillis());
+ updateFocusAppearanceLocked(service.getFocusStrokeWidth(), service.getFocusColor());
mService = service;
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
@@ -1635,6 +1686,40 @@ public final class AccessibilityManager {
}
/**
+ * Updates the stroke width and color of the focus rectangle.
+ *
+ * @param strokeWidth The strokeWidth of the focus rectangle.
+ * @param color The color of the focus rectangle.
+ */
+ private void updateFocusAppearanceLocked(int strokeWidth, int color) {
+ if (mFocusStrokeWidth == strokeWidth && mFocusColor == color) {
+ return;
+ }
+ mFocusStrokeWidth = strokeWidth;
+ mFocusColor = color;
+ }
+
+ /**
+ * Sets the stroke width and color of the focus rectangle to default value.
+ *
+ * @param resource The resources.
+ */
+ private void initialFocusAppearanceLocked(Resources resource) {
+ try {
+ mFocusStrokeWidth = resource.getDimensionPixelSize(
+ R.dimen.accessibility_focus_highlight_stroke_width);
+ mFocusColor = resource.getColor(R.color.accessibility_focus_highlight_color);
+ } catch (Resources.NotFoundException re) {
+ // Sets the stroke width and color to default value by hardcoded for making
+ // the Talkback can work normally.
+ mFocusStrokeWidth = (int) (4 * resource.getDisplayMetrics().density);
+ mFocusColor = 0xbf39b500;
+ Log.e(LOG_TAG, "Error while initialing the focus appearance data then setting to"
+ + " default value by hardcoded", re);
+ }
+ }
+
+ /**
* Determines if the accessibility button within the system navigation area is supported.
*
* @return {@code true} if the accessibility button is supported on this device,
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 5d3c72015ee3..c71ea53c414d 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -92,4 +92,8 @@ interface IAccessibilityManager {
void associateEmbeddedHierarchy(IBinder host, IBinder embedded);
void disassociateEmbeddedHierarchy(IBinder token);
+
+ int getFocusStrokeWidth();
+
+ int getFocusColor();
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
index 94b9ad1c3279..041399ccb8ec 100644
--- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -29,4 +29,6 @@ oneway interface IAccessibilityManagerClient {
void notifyServicesStateChanged(long updatedUiTimeout);
void setRelevantEventTypes(int eventTypes);
+
+ void setFocusAppearance(int strokeWidth, int color);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 907b5b085b59..eaf72dce62fe 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -598,6 +598,9 @@ public final class InputMethodManager {
*/
@Override
public void finishInput() {
+ ImeTracing.getInstance().triggerClientDump(
+ "InputMethodManager.DelegateImpl#finishInput", InputMethodManager.this,
+ null /* icProto */);
synchronized (mH) {
finishInputLocked();
}
@@ -641,6 +644,10 @@ public final class InputMethodManager {
int startInputFlags = getStartInputFlags(focusedView, 0);
startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS;
+ ImeTracing.getInstance().triggerClientDump(
+ "InputMethodManager.DelegateImpl#startInputAsyncOnWindowFocusGain",
+ InputMethodManager.this, null /* icProto */);
+
final ImeFocusController controller = getFocusController();
if (controller == null) {
return;
@@ -950,6 +957,9 @@ public final class InputMethodManager {
case MSG_APPLY_IME_VISIBILITY: {
synchronized (mH) {
if (mImeInsetsConsumer != null) {
+ ImeTracing.getInstance().triggerClientDump(
+ "ImeInsetsSourceConsumer#applyImeVisibility",
+ InputMethodManager.this, null /* icProto */);
mImeInsetsConsumer.applyImeVisibility(msg.arg1 != 0);
}
}
@@ -1860,6 +1870,9 @@ public final class InputMethodManager {
* {@link #HIDE_NOT_ALWAYS} bit set.
**/
public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
+ ImeTracing.getInstance().triggerClientDump(
+ "InputMethodManager#toggleSoftInputFromWindow", InputMethodManager.this,
+ null /* icProto */);
synchronized (mH) {
final View servedView = getServedViewLocked();
if (servedView == null || servedView.getWindowToken() != windowToken) {
@@ -1887,6 +1900,9 @@ public final class InputMethodManager {
* {@link #HIDE_NOT_ALWAYS} bit set.
*/
public void toggleSoftInput(int showFlags, int hideFlags) {
+ ImeTracing.getInstance().triggerClientDump(
+ "InputMethodManager#toggleSoftInput", InputMethodManager.this,
+ null /* icProto */);
if (mCurMethod != null) {
try {
mCurMethod.toggleSoftInput(showFlags, hideFlags);
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index d8a632d10bc3..5b32649ac55b 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -45,15 +45,19 @@ public final class TextSelection implements Parcelable {
private final int mEndIndex;
private final EntityConfidence mEntityConfidence;
@Nullable private final String mId;
+ @Nullable
+ private final TextClassification mTextClassification;
private final Bundle mExtras;
private TextSelection(
int startIndex, int endIndex, Map<String, Float> entityConfidence, String id,
+ @Nullable TextClassification textClassification,
Bundle extras) {
mStartIndex = startIndex;
mEndIndex = endIndex;
mEntityConfidence = new EntityConfidence(entityConfidence);
mId = id;
+ mTextClassification = textClassification;
mExtras = extras;
}
@@ -111,6 +115,19 @@ public final class TextSelection implements Parcelable {
}
/**
+ * Returns the text classification result of the suggested selection span. Enables the text
+ * classification by calling
+ * {@link TextSelection.Request.Builder#setIncludeTextClassification(boolean)}. If the text
+ * classifier does not support it, a {@code null} is returned.
+ *
+ * @see TextSelection.Request.Builder#setIncludeTextClassification(boolean)
+ */
+ @Nullable
+ public TextClassification getTextClassification() {
+ return mTextClassification;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -138,6 +155,8 @@ public final class TextSelection implements Parcelable {
private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
@Nullable private String mId;
@Nullable
+ private TextClassification mTextClassification;
+ @Nullable
private Bundle mExtras;
/**
@@ -179,6 +198,21 @@ public final class TextSelection implements Parcelable {
}
/**
+ * Sets the text classification result of the suggested selection. If
+ * {@link Request#shouldIncludeTextClassification()} is {@code true}, set this value.
+ * Otherwise this value may be set to null. The intention of this method is to avoid
+ * doing expensive work if the client is not interested in such result.
+ *
+ * @return this builder
+ * @see Request#shouldIncludeTextClassification()
+ */
+ @NonNull
+ public Builder setTextClassification(@Nullable TextClassification textClassification) {
+ mTextClassification = textClassification;
+ return this;
+ }
+
+ /**
* Sets the extended data.
*
* @return this builder
@@ -196,7 +230,7 @@ public final class TextSelection implements Parcelable {
public TextSelection build() {
return new TextSelection(
mStartIndex, mEndIndex, mEntityConfidence, mId,
- mExtras == null ? Bundle.EMPTY : mExtras);
+ mTextClassification, mExtras == null ? Bundle.EMPTY : mExtras);
}
}
@@ -210,6 +244,7 @@ public final class TextSelection implements Parcelable {
private final int mEndIndex;
@Nullable private final LocaleList mDefaultLocales;
private final boolean mDarkLaunchAllowed;
+ private final boolean mIncludeTextClassification;
private final Bundle mExtras;
@Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
@@ -219,12 +254,14 @@ public final class TextSelection implements Parcelable {
int endIndex,
LocaleList defaultLocales,
boolean darkLaunchAllowed,
+ boolean includeTextClassification,
Bundle extras) {
mText = text;
mStartIndex = startIndex;
mEndIndex = endIndex;
mDefaultLocales = defaultLocales;
mDarkLaunchAllowed = darkLaunchAllowed;
+ mIncludeTextClassification = includeTextClassification;
mExtras = extras;
}
@@ -303,6 +340,14 @@ public final class TextSelection implements Parcelable {
}
/**
+ * Returns true if the client wants the text classifier to classify the text as well and
+ * include a {@link TextClassification} object in the result.
+ */
+ public boolean shouldIncludeTextClassification() {
+ return mIncludeTextClassification;
+ }
+
+ /**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
@@ -320,9 +365,9 @@ public final class TextSelection implements Parcelable {
private final CharSequence mText;
private final int mStartIndex;
private final int mEndIndex;
-
@Nullable private LocaleList mDefaultLocales;
private boolean mDarkLaunchAllowed;
+ private boolean mIncludeTextClassification;
private Bundle mExtras;
/**
@@ -372,6 +417,21 @@ public final class TextSelection implements Parcelable {
}
/**
+ * @param includeTextClassification If true, suggests the TextClassifier to classify the
+ * text in the suggested selection span and include a TextClassification object in
+ * the result. The TextClassifier may not support this and in which case,
+ * {@link TextSelection#getTextClassification()} returns {@code null}.
+ *
+ * @return this builder.
+ * @see TextSelection#getTextClassification()
+ */
+ @NonNull
+ public Builder setIncludeTextClassification(boolean includeTextClassification) {
+ mIncludeTextClassification = includeTextClassification;
+ return this;
+ }
+
+ /**
* Sets the extended data.
*
* @return this builder
@@ -389,6 +449,7 @@ public final class TextSelection implements Parcelable {
public Request build() {
return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
mDefaultLocales, mDarkLaunchAllowed,
+ mIncludeTextClassification,
mExtras == null ? Bundle.EMPTY : mExtras);
}
}
@@ -406,6 +467,7 @@ public final class TextSelection implements Parcelable {
dest.writeParcelable(mDefaultLocales, flags);
dest.writeBundle(mExtras);
dest.writeParcelable(mSystemTcMetadata, flags);
+ dest.writeBoolean(mIncludeTextClassification);
}
private static Request readFromParcel(Parcel in) {
@@ -415,9 +477,10 @@ public final class TextSelection implements Parcelable {
final LocaleList defaultLocales = in.readParcelable(null);
final Bundle extras = in.readBundle();
final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
+ final boolean includeTextClassification = in.readBoolean();
final Request request = new Request(text, startIndex, endIndex, defaultLocales,
- /* darkLaunchAllowed= */ false, extras);
+ /* darkLaunchAllowed= */ false, includeTextClassification, extras);
request.setSystemTextClassifierMetadata(systemTcMetadata);
return request;
}
@@ -448,6 +511,7 @@ public final class TextSelection implements Parcelable {
mEntityConfidence.writeToParcel(dest, flags);
dest.writeString(mId);
dest.writeBundle(mExtras);
+ dest.writeParcelable(mTextClassification, flags);
}
public static final @android.annotation.NonNull Parcelable.Creator<TextSelection> CREATOR =
@@ -469,5 +533,6 @@ public final class TextSelection implements Parcelable {
mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
mId = in.readString();
mExtras = in.readBundle();
+ mTextClassification = in.readParcelable(TextClassification.class.getClassLoader());
}
}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index ea906c68c69b..ce29eea9fd0c 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1409,6 +1409,11 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
return mFilter;
}
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return AutoCompleteTextView.class.getName();
+ }
+
private class DropDownItemClickListener implements AdapterView.OnItemClickListener {
public void onItemClick(AdapterView parent, View v, int position, long id) {
performCompletion(v, position, id);
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index e08ccfddc4c5..6f189204434a 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -1124,6 +1124,7 @@ public final class SelectionActionModeHelper {
mTrimmedText, mRelativeStart, mRelativeEnd)
.setDefaultLocales(mDefaultLocales)
.setDarkLaunchAllowed(true)
+ .setIncludeTextClassification(true)
.build();
selection = mTextClassifier.get().suggestSelection(request);
} else {
@@ -1181,6 +1182,8 @@ public final class SelectionActionModeHelper {
// Do not show smart actions for text containing unsupported characters.
android.util.EventLog.writeEvent(0x534e4554, "116321860", -1, "");
classification = TextClassification.EMPTY;
+ } else if (selection != null && selection.getTextClassification() != null) {
+ classification = selection.getTextClassification();
} else if (mContext.getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.P) {
final TextClassification.Request request =
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index eda168dd8553..c4bdb5a72689 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -213,12 +213,12 @@ public class TaskOrganizer extends WindowOrganizer {
private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
@Override
public void addStartingWindow(ActivityManager.RunningTaskInfo taskInfo, IBinder appToken) {
- TaskOrganizer.this.addStartingWindow(taskInfo, appToken);
+ mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(taskInfo, appToken));
}
@Override
public void removeStartingWindow(ActivityManager.RunningTaskInfo taskInfo) {
- TaskOrganizer.this.removeStartingWindow(taskInfo);
+ mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskInfo));
}
@Override
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2e3d560ccb56..9668c3b0af1c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -162,6 +162,9 @@ public class ChooserActivity extends ResolverActivity implements
private AppPredictor mWorkAppPredictor;
private boolean mShouldDisplayLandscape;
+ private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
+ private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
+
@UnsupportedAppUsage
public ChooserActivity() {
}
@@ -905,7 +908,7 @@ public class ChooserActivity extends ResolverActivity implements
adapter,
getPersonalProfileUserHandle(),
/* workProfileUserHandle= */ null,
- isSendAction(getTargetIntent()));
+ isSendAction(getTargetIntent()), getMaxTargetsPerRow());
}
private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
@@ -934,7 +937,7 @@ public class ChooserActivity extends ResolverActivity implements
selectedProfile,
getPersonalProfileUserHandle(),
getWorkProfileUserHandle(),
- isSendAction(getTargetIntent()));
+ isSendAction(getTargetIntent()), getMaxTargetsPerRow());
}
private int findSelectedProfile() {
@@ -2683,7 +2686,7 @@ public class ChooserActivity extends ResolverActivity implements
// and b/150936654
recyclerView.setAdapter(gridAdapter);
((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(
- gridAdapter.getMaxTargetsPerRow());
+ getMaxTargetsPerRow());
}
UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle();
@@ -2848,9 +2851,7 @@ public class ChooserActivity extends ResolverActivity implements
@Override // ChooserListCommunicator
public int getMaxRankedTargets() {
- return mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() == null
- ? ChooserGridAdapter.MAX_TARGETS_PER_ROW_PORTRAIT
- : mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().getMaxTargetsPerRow();
+ return getMaxTargetsPerRow();
}
@Override // ChooserListCommunicator
@@ -3198,6 +3199,13 @@ public class ChooserActivity extends ResolverActivity implements
}
}
+ int getMaxTargetsPerRow() {
+ int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
+ if (mShouldDisplayLandscape) {
+ maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
+ }
+ return maxTargets;
+ }
/**
* Adapter for all types of items and targets in ShareSheet.
* Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
@@ -3226,9 +3234,6 @@ public class ChooserActivity extends ResolverActivity implements
private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
private static final int VIEW_TYPE_FOOTER = 6;
- private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
- private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
-
private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
ChooserGridAdapter(ChooserListAdapter wrappedAdapter) {
@@ -3277,14 +3282,6 @@ public class ChooserActivity extends ResolverActivity implements
return false;
}
- int getMaxTargetsPerRow() {
- int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
- if (mShouldDisplayLandscape) {
- maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
- }
- return maxTargets;
- }
-
/**
* Hides the list item content preview.
* <p>Not to be confused with the sticky content preview which is above the
@@ -3654,8 +3651,7 @@ public class ChooserActivity extends ResolverActivity implements
position -= getSystemRowCount() + getProfileRowCount();
final int serviceCount = mChooserListAdapter.getServiceTargetCount();
- final int serviceRows = (int) Math.ceil((float) serviceCount
- / ChooserListAdapter.MAX_SERVICE_TARGETS);
+ final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets());
if (position < serviceRows) {
return position * getMaxTargetsPerRow();
}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 00b5cb646bca..570066807f16 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -82,8 +82,6 @@ public class ChooserListAdapter extends ResolverListAdapter {
private static final int MAX_SERVICE_TARGET_APP = 8;
private static final int DEFAULT_DIRECT_SHARE_RANKING_SCORE = 1000;
- static final int MAX_SERVICE_TARGETS = 8;
-
/** {@link #getBaseScore} */
public static final float CALLER_TARGET_SCORE_BOOST = 900.f;
/** {@link #getBaseScore} */
@@ -130,10 +128,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
super(context, payloadIntents, null, rList, filterLastUsed,
resolverListController, chooserListCommunicator, false);
- createPlaceHolders();
mMaxShortcutTargetsPerApp =
context.getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp);
mChooserListCommunicator = chooserListCommunicator;
+ createPlaceHolders();
mSelectableTargetInfoCommunicator = selectableTargetInfoCommunicator;
if (initialIntents != null) {
@@ -227,7 +225,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
mParkingDirectShareTargets.clear();
mPendingChooserTargetService.clear();
mShortcutComponents.clear();
- for (int i = 0; i < MAX_SERVICE_TARGETS; i++) {
+ for (int i = 0; i < mChooserListCommunicator.getMaxRankedTargets(); i++) {
mServiceTargets.add(mPlaceHolderTargetInfo);
}
}
@@ -382,7 +380,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
public int getServiceTargetCount() {
if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent())
&& !ActivityManager.isLowRamDeviceStatic()) {
- return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS);
+ return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets());
}
return 0;
@@ -847,7 +845,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
int currentSize = mServiceTargets.size();
final float newScore = chooserTargetInfo.getModifiedScore();
- for (int i = 0; i < Math.min(currentSize, MAX_SERVICE_TARGETS); i++) {
+ for (int i = 0; i < Math.min(currentSize, mChooserListCommunicator.getMaxRankedTargets());
+ i++) {
final ChooserTargetInfo serviceTarget = mServiceTargets.get(i);
if (serviceTarget == null) {
mServiceTargets.set(i, chooserTargetInfo);
@@ -858,7 +857,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
}
- if (currentSize < MAX_SERVICE_TARGETS) {
+ if (currentSize < mChooserListCommunicator.getMaxRankedTargets()) {
mServiceTargets.add(chooserTargetInfo);
return true;
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 3a65a324f9d6..dd837fc2194c 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -39,17 +39,19 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
private final ChooserProfileDescriptor[] mItems;
private final boolean mIsSendAction;
private int mBottomOffset;
+ private int mMaxTargetsPerRow;
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter adapter,
UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle,
- boolean isSendAction) {
+ boolean isSendAction, int maxTargetsPerRow) {
super(context, /* currentPage */ 0, personalProfileUserHandle, workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
createProfileDescriptor(adapter)
};
mIsSendAction = isSendAction;
+ mMaxTargetsPerRow = maxTargetsPerRow;
}
ChooserMultiProfilePagerAdapter(Context context,
@@ -58,7 +60,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Profile int defaultProfile,
UserHandle personalProfileUserHandle,
UserHandle workProfileUserHandle,
- boolean isSendAction) {
+ boolean isSendAction, int maxTargetsPerRow) {
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
workProfileUserHandle);
mItems = new ChooserProfileDescriptor[] {
@@ -66,6 +68,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
createProfileDescriptor(workAdapter)
};
mIsSendAction = isSendAction;
+ mMaxTargetsPerRow = maxTargetsPerRow;
}
private ChooserProfileDescriptor createProfileDescriptor(
@@ -114,7 +117,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
ChooserActivity.ChooserGridAdapter chooserGridAdapter =
getItem(pageIndex).chooserGridAdapter;
GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager();
- glm.setSpanCount(chooserGridAdapter.getMaxTargetsPerRow());
+ glm.setSpanCount(mMaxTargetsPerRow);
glm.setSpanSizeLookup(
new GridLayoutManager.SpanSizeLookup() {
@Override
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 6d98a5982c4f..0b047a2a3d47 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -19,6 +19,7 @@ package com.android.internal.app;
import com.android.internal.os.BatteryStatsImpl;
import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.os.BatteryUsageStats;
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
@@ -49,6 +50,9 @@ interface IBatteryStats {
void noteResetFlashlight();
// Remaining methods are only used in Java.
+
+ BatteryUsageStats getBatteryUsageStats();
+
@UnsupportedAppUsage
byte[] getStatistics();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dcd74fd1ad3e..fb6eb97253e3 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -90,6 +90,8 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.Display;
@@ -112,7 +114,6 @@ import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -10830,8 +10831,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
final ByteArrayOutputStream memStream = new ByteArrayOutputStream();
try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(memStream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(memStream);
writeDailyItemsLocked(out);
final long initialTimeMs = SystemClock.uptimeMillis() - startTimeMs;
BackgroundThread.getHandler().post(new Runnable() {
@@ -10861,15 +10861,15 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- private void writeDailyItemsLocked(XmlSerializer out) throws IOException {
+ private void writeDailyItemsLocked(TypedXmlSerializer out) throws IOException {
StringBuilder sb = new StringBuilder(64);
out.startDocument(null, true);
out.startTag(null, "daily-items");
for (int i=0; i<mDailyItems.size(); i++) {
final DailyItem dit = mDailyItems.get(i);
out.startTag(null, "item");
- out.attribute(null, "start", Long.toString(dit.mStartTime));
- out.attribute(null, "end", Long.toString(dit.mEndTime));
+ out.attributeLong(null, "start", dit.mStartTime);
+ out.attributeLong(null, "end", dit.mEndTime);
writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb);
writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb);
if (dit.mPackageChanges != null) {
@@ -10878,7 +10878,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (pc.mUpdate) {
out.startTag(null, "upd");
out.attribute(null, "pkg", pc.mPackageName);
- out.attribute(null, "ver", Long.toString(pc.mVersionCode));
+ out.attributeLong(null, "ver", pc.mVersionCode);
out.endTag(null, "upd");
} else {
out.startTag(null, "rem");
@@ -10893,11 +10893,11 @@ public class BatteryStatsImpl extends BatteryStats {
out.endDocument();
}
- private void writeDailyLevelSteps(XmlSerializer out, String tag, LevelStepTracker steps,
+ private void writeDailyLevelSteps(TypedXmlSerializer out, String tag, LevelStepTracker steps,
StringBuilder tmpBuilder) throws IOException {
if (steps != null) {
out.startTag(null, tag);
- out.attribute(null, "n", Integer.toString(steps.mNumStepDurations));
+ out.attributeInt(null, "n", steps.mNumStepDurations);
for (int i=0; i<steps.mNumStepDurations; i++) {
out.startTag(null, "s");
tmpBuilder.setLength(0);
@@ -10919,10 +10919,9 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
readDailyItemsLocked(parser);
- } catch (XmlPullParserException e) {
+ } catch (IOException e) {
} finally {
try {
stream.close();
@@ -10931,7 +10930,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- private void readDailyItemsLocked(XmlPullParser parser) {
+ private void readDailyItemsLocked(TypedXmlPullParser parser) {
try {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -10975,17 +10974,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- void readDailyItemTagLocked(XmlPullParser parser) throws NumberFormatException,
+ void readDailyItemTagLocked(TypedXmlPullParser parser) throws NumberFormatException,
XmlPullParserException, IOException {
DailyItem dit = new DailyItem();
- String attr = parser.getAttributeValue(null, "start");
- if (attr != null) {
- dit.mStartTime = Long.parseLong(attr);
- }
- attr = parser.getAttributeValue(null, "end");
- if (attr != null) {
- dit.mEndTime = Long.parseLong(attr);
- }
+ dit.mStartTime = parser.getAttributeLong(null, "start", 0);
+ dit.mEndTime = parser.getAttributeLong(null, "end", 0);
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -11006,8 +10999,7 @@ public class BatteryStatsImpl extends BatteryStats {
PackageChange pc = new PackageChange();
pc.mUpdate = true;
pc.mPackageName = parser.getAttributeValue(null, "pkg");
- String verStr = parser.getAttributeValue(null, "ver");
- pc.mVersionCode = verStr != null ? Long.parseLong(verStr) : 0;
+ pc.mVersionCode = parser.getAttributeLong(null, "ver", 0);
dit.mPackageChanges.add(pc);
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("rem")) {
@@ -11028,16 +11020,15 @@ public class BatteryStatsImpl extends BatteryStats {
mDailyItems.add(dit);
}
- void readDailyItemTagDetailsLocked(XmlPullParser parser, DailyItem dit, boolean isCharge,
+ void readDailyItemTagDetailsLocked(TypedXmlPullParser parser, DailyItem dit, boolean isCharge,
String tag)
throws NumberFormatException, XmlPullParserException, IOException {
- final String numAttr = parser.getAttributeValue(null, "n");
- if (numAttr == null) {
+ final int num = parser.getAttributeInt(null, "n", -1);
+ if (num == -1) {
Slog.w(TAG, "Missing 'n' attribute at " + parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
return;
}
- final int num = Integer.parseInt(numAttr);
LevelStepTracker steps = new LevelStepTracker(num);
if (isCharge) {
dit.mChargeSteps = steps;
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
new file mode 100644
index 000000000000..62e9f98181ab
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.content.Context;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.Bundle;
+import android.os.UidBatteryConsumer;
+import android.os.UserManager;
+
+import java.util.List;
+
+/**
+ * Uses accumulated battery stats data and PowerCalculators to produce power
+ * usage data attributed to subsystems and UIDs.
+ */
+public class BatteryUsageStatsProvider {
+ private final Context mContext;
+ private final BatteryStatsImpl mStats;
+
+ public BatteryUsageStatsProvider(Context context, BatteryStatsImpl stats) {
+ mContext = context;
+ mStats = stats;
+ }
+
+ /**
+ * Returns a snapshot of battery attribution data.
+ */
+ public BatteryUsageStats getBatteryUsageStats() {
+
+ // TODO(b/174186345): instead of BatteryStatsHelper, use PowerCalculators directly.
+ final BatteryStatsHelper batteryStatsHelper = new BatteryStatsHelper(mContext,
+ false /* collectBatteryBroadcast */);
+ batteryStatsHelper.create((Bundle) null);
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ batteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+ userManager.getUserProfiles());
+
+ // TODO(b/174186358): read extra power component number from configuration
+ final int customPowerComponentCount = 0;
+ final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder()
+ .setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
+ .setConsumedPower(batteryStatsHelper.getTotalPower());
+
+ final List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
+ for (int i = 0; i < usageList.size(); i++) {
+ final BatterySipper sipper = usageList.get(i);
+ if (sipper.drainType == BatterySipper.DrainType.APP) {
+ batteryUsageStatsBuilder.addUidBatteryConsumer(
+ new UidBatteryConsumer.Builder(customPowerComponentCount, sipper.getUid())
+ .setPackageWithHighestDrain(sipper.packageWithHighestDrain)
+ .setConsumedPower(sipper.sumPower())
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU,
+ sipper.cpuPowerMah)
+ .build());
+ }
+ }
+ return batteryUsageStatsBuilder.build();
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index f668bbaf9438..e595db384cb9 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -184,6 +184,7 @@ public class KernelWakelockReader {
try {
wlStats = mSuspendControlService.getWakeLockStats();
+ Slog.i(TAG, "Number of wakelock obtained from SystemSuspend: " + wlStats.length);
updateWakelockStats(wlStats, staleStats);
} catch (RemoteException e) {
Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e);
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 44dca9bae3da..854fb17e692b 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -69,6 +69,6 @@ oneway interface IPhoneStateListener {
void onRegistrationFailed(in CellIdentity cellIdentity,
String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
void onBarringInfoChanged(in BarringInfo barringInfo);
- void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
-
+ void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs);
+ void onDataEnabledChanged(boolean enabled, int reason);
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index a28a66376497..c2cbc04fcee5 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -41,16 +41,9 @@ interface ITelephonyRegistry {
IOnSubscriptionsChangedListener callback);
void removeOnSubscriptionsChangedListener(String pkg,
IOnSubscriptionsChangedListener callback);
- /**
- * @deprecated Use {@link #listenWithFeature(String, String, IPhoneStateListener, int,
- * boolean) instead
- */
- @UnsupportedAppUsage
- void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
- void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, long events,
- boolean notifyNow);
- void listenForSubscriber(in int subId, String pkg, String featureId,
- IPhoneStateListener callback, long events, boolean notifyNow);
+
+ void listenWithEventList(in int subId, String pkg, String featureId,
+ IPhoneStateListener callback, in int[] events, boolean notifyNow);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void notifyCallStateForAllSubs(int state, String incomingNumber);
void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber);
@@ -99,6 +92,6 @@ interface ITelephonyRegistry {
void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo);
- void notifyPhysicalChannelConfigurationForSubscriber(in int subId,
+ void notifyPhysicalChannelConfigForSubscriber(in int subId,
in List<PhysicalChannelConfig> configs);
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index e067f5fbdf45..5388235df664 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -208,6 +208,13 @@ public class ArrayUtils {
}
/**
+ * Length of the given map or 0 if it's null.
+ */
+ public static int size(@Nullable Map<?, ?> map) {
+ return map == null ? 0 : map.size();
+ }
+
+ /**
* Checks that value is present as at least one of the elements of the array.
* @param array the array to check in
* @param value the value to check for
diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java
index d3fcf71ba399..bc3b1f8bef57 100644
--- a/core/java/com/android/internal/util/BinaryXmlSerializer.java
+++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java
@@ -69,7 +69,7 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
* {@code ABX_}, representing "Android Binary XML." The final byte is a
* version number which may be incremented as the protocol changes.
*/
- static final byte[] PROTOCOL_MAGIC_VERSION_0 = new byte[] { 0x41, 0x42, 0x58, 0x00 };
+ public static final byte[] PROTOCOL_MAGIC_VERSION_0 = new byte[] { 0x41, 0x42, 0x58, 0x00 };
/**
* Internal token which represents an attribute associated with the most
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 8eb0e280350f..244efc596926 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -53,6 +53,7 @@ import java.util.Set;
public class XmlUtils {
private static final String STRING_ARRAY_SEPARATOR = ":";
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
private static class ForcedTypedXmlSerializer extends XmlSerializerWrapper
implements TypedXmlSerializer {
public ForcedTypedXmlSerializer(XmlSerializer wrapped) {
@@ -133,6 +134,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
private static class ForcedTypedXmlPullParser extends XmlPullParserWrapper
implements TypedXmlPullParser {
public ForcedTypedXmlPullParser(XmlPullParser wrapped) {
@@ -1715,6 +1717,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
if (in instanceof TypedXmlPullParser) {
return ((TypedXmlPullParser) in).getAttributeInt(null, name, defaultValue);
@@ -1730,6 +1733,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
if (in instanceof TypedXmlPullParser) {
try {
@@ -1746,6 +1750,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static void writeIntAttribute(XmlSerializer out, String name, int value)
throws IOException {
if (out instanceof TypedXmlSerializer) {
@@ -1755,6 +1760,7 @@ public class XmlUtils {
out.attribute(null, name, Integer.toString(value));
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
if (in instanceof TypedXmlPullParser) {
return ((TypedXmlPullParser) in).getAttributeLong(null, name, defaultValue);
@@ -1770,6 +1776,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
if (in instanceof TypedXmlPullParser) {
try {
@@ -1786,6 +1793,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static void writeLongAttribute(XmlSerializer out, String name, long value)
throws IOException {
if (out instanceof TypedXmlSerializer) {
@@ -1795,6 +1803,7 @@ public class XmlUtils {
out.attribute(null, name, Long.toString(value));
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static float readFloatAttribute(XmlPullParser in, String name) throws IOException {
if (in instanceof TypedXmlPullParser) {
try {
@@ -1811,6 +1820,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static void writeFloatAttribute(XmlSerializer out, String name, float value)
throws IOException {
if (out instanceof TypedXmlSerializer) {
@@ -1820,10 +1830,12 @@ public class XmlUtils {
out.attribute(null, name, Float.toString(value));
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static boolean readBooleanAttribute(XmlPullParser in, String name) {
return readBooleanAttribute(in, name, false);
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static boolean readBooleanAttribute(XmlPullParser in, String name,
boolean defaultValue) {
if (in instanceof TypedXmlPullParser) {
@@ -1837,6 +1849,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
throws IOException {
if (out instanceof TypedXmlSerializer) {
@@ -1869,6 +1882,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
if (in instanceof TypedXmlPullParser) {
try {
@@ -1885,6 +1899,7 @@ public class XmlUtils {
}
}
+ @SuppressWarnings("AndroidFrameworkEfficientXml")
public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)
throws IOException {
if (value != null) {
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index 21021457377a..362fd7b4e937 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -18,9 +18,7 @@ package com.android.internal.widget;
import android.content.Context;
import android.graphics.Rect;
-import android.os.RemoteException;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
@@ -72,7 +70,6 @@ import java.util.ArrayList;
*/
public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
GestureDetector.OnGestureListener {
- private final static String TAG = "DecorCaptionView";
private PhoneWindow mOwner = null;
private boolean mShow = false;
@@ -327,11 +324,7 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
private void toggleFreeformWindowingMode() {
Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
if (callback != null) {
- try {
- callback.toggleFreeformWindowingMode();
- } catch (RemoteException ex) {
- Log.e(TAG, "Cannot change task workspace.");
- }
+ callback.toggleFreeformWindowingMode();
}
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index ac2361dff560..b4d8e506c61a 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -35,24 +35,23 @@ import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
@@ -712,8 +711,7 @@ public class BootReceiver extends BroadcastReceiver {
HashMap<String, Long> timestamps = new HashMap<String, Long>();
boolean success = false;
try (final FileInputStream stream = sFile.openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -735,8 +733,7 @@ public class BootReceiver extends BroadcastReceiver {
String tagName = parser.getName();
if (tagName.equals("log")) {
final String filename = parser.getAttributeValue(null, "filename");
- final long timestamp = Long.valueOf(parser.getAttributeValue(
- null, "timestamp"));
+ final long timestamp = parser.getAttributeLong(null, "timestamp");
timestamps.put(filename, timestamp);
} else {
Slog.w(TAG, "Unknown tag: " + parser.getName());
@@ -775,8 +772,7 @@ public class BootReceiver extends BroadcastReceiver {
}
try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
+ TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "log-files");
@@ -785,7 +781,7 @@ public class BootReceiver extends BroadcastReceiver {
String filename = itor.next();
out.startTag(null, "log");
out.attribute(null, "filename", filename);
- out.attribute(null, "timestamp", timestamps.get(filename).toString());
+ out.attributeLong(null, "timestamp", timestamps.get(filename));
out.endTag(null, "log");
}
diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java
index c7c423b82cf1..4d1949e3e3d7 100644
--- a/core/java/com/android/server/backup/PermissionBackupHelper.java
+++ b/core/java/com/android/server/backup/PermissionBackupHelper.java
@@ -17,8 +17,8 @@
package com.android.server.backup;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.backup.BlobBackupHelper;
-import android.os.UserHandle;
import android.permission.PermissionManagerInternal;
import android.util.Slog;
@@ -34,14 +34,14 @@ public class PermissionBackupHelper extends BlobBackupHelper {
// key under which the permission-grant state blob is committed to backup
private static final String KEY_PERMISSIONS = "permissions";
- private final @NonNull UserHandle mUser;
+ private final @UserIdInt int mUserId;
private final @NonNull PermissionManagerInternal mPermissionManager;
public PermissionBackupHelper(int userId) {
super(STATE_VERSION, KEY_PERMISSIONS);
- mUser = UserHandle.of(userId);
+ mUserId = userId;
mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
}
@@ -53,7 +53,7 @@ public class PermissionBackupHelper extends BlobBackupHelper {
try {
switch (key) {
case KEY_PERMISSIONS:
- return mPermissionManager.backupRuntimePermissions(mUser);
+ return mPermissionManager.backupRuntimePermissions(mUserId);
default:
Slog.w(TAG, "Unexpected backup key " + key);
@@ -72,7 +72,7 @@ public class PermissionBackupHelper extends BlobBackupHelper {
try {
switch (key) {
case KEY_PERMISSIONS:
- mPermissionManager.restoreRuntimePermissions(payload, mUser);
+ mPermissionManager.restoreRuntimePermissions(payload, mUserId);
break;
default:
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 5a859aabce7b..bf79a4472fa5 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -571,6 +571,9 @@ enum {
MEMINFO_PAGE_TABLES,
MEMINFO_KERNEL_STACK,
MEMINFO_KERNEL_RECLAIMABLE,
+ MEMINFO_ACTIVE,
+ MEMINFO_INACTIVE,
+ MEMINFO_UNEVICTABLE,
MEMINFO_COUNT
};
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index d18722049109..91131115da83 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -313,7 +313,41 @@ message ConstantsProto {
// The minimum amount of time between quota check alarms.
optional int64 min_quota_check_delay_ms = 23;
- // Next tag: 24
+ // The total session limit of the particular standby bucket. Apps in this standby bucket can
+ // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ // HPJs).
+ optional int64 hpj_limit_active_ms = 24;
+ // The total session limit of the particular standby bucket. Apps in this standby bucket can
+ // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ // HPJs).
+ optional int64 hpj_limit_working_ms = 25;
+ // The total session limit of the particular standby bucket. Apps in this standby bucket can
+ // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ // HPJs).
+ optional int64 hpj_limit_frequent_ms = 26;
+ // The total session limit of the particular standby bucket. Apps in this standby bucket can
+ // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ // HPJs).
+ optional int64 hpj_limit_rare_ms = 27;
+ // The total session limit of the particular standby bucket. Apps in this standby bucket can
+ // only have HPJ sessions totalling HPJ_LIMIT (without factoring in any rewards or free
+ // HPJs).
+ optional int64 hpj_limit_restricted_ms = 28;
+ // The period of time used to calculate HPJ sessions. Apps can only have HPJ sessions
+ // totalling HPJ_LIMIT_<bucket>_MS within this period of time (without factoring in any
+ // rewards or free HPJs).
+ optional int64 hpj_window_size_ms = 29;
+ // Length of time used to split an app's top time into chunks.
+ optional int64 hpj_top_app_time_chunk_size_ms = 30;
+ // How much HPJ quota to give back to an app based on the number of top app time chunks
+ // it had.
+ optional int64 hpj_reward_top_app_ms = 31;
+ // How much HPJ quota to give back to an app based on each non-top user interaction.
+ optional int64 hpj_reward_interaction_ms = 32;
+ // How much HPJ quota to give back to an app based on each notification seen event.
+ optional int64 hpj_reward_notification_seen_ms = 33;
+
+ // Next tag: 34
}
optional QuotaController quota_controller = 24;
@@ -560,6 +594,11 @@ message StateControllerProto {
// The amount of time that this job has remaining in its quota. This
// can be negative if the job is out of quota.
optional int64 remaining_quota_ms = 6;
+ // True if the app has requested that this be a foreground job.
+ optional bool is_requested_foreground_job = 7;
+ // True if this job is within the foreground quota bounds and is therefore allowed to
+ // run as a foreground job. Valid only if is_foreground_requested_job is true.
+ optional bool is_within_fg_job_quota = 8;
}
repeated TrackedJob tracked_jobs = 4;
@@ -665,6 +704,19 @@ message StateControllerProto {
repeated JobStatusShortInfoProto running_jobs = 5;
}
+ message TopAppTimer {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional Package pkg = 1;
+ // True if the Timer is actively tracking jobs.
+ optional bool is_active = 2;
+ // The time this timer last became active. Only valid if is_active is true.
+ optional int64 start_time_elapsed = 3;
+ // How many activities are currently in the RESUMED state. Valid only if is_active is
+ // true.
+ optional int32 activity_count = 4;
+ }
+
message PackageStats {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -677,6 +729,8 @@ message StateControllerProto {
repeated ExecutionStats execution_stats = 4;
reserved 5; // in_quota_alarm_listener
+
+ optional Timer fg_job_timer = 6;
}
repeated PackageStats package_stats = 5;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60dd0eb8a780..1250eb776176 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4047,6 +4047,13 @@
<permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"
android:protectionLevel="signature" />
+ <!-- Allows an application to modify the refresh rate switching type. This
+ matches Setting.Secure.MATCH_CONTENT_FRAME_RATE.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -5255,7 +5262,8 @@
<attribution android:tag="TwilightService" android:label="@string/twilight_service"/>
<!-- Attribution for the Offline LocationTimeZoneProvider, used to detect time zone using
on-device data -->
- <attribution android:tag="OfflineLocationTimeZoneProvider" android:label="@string/offline_location_time_zone_detection_service"/>
+ <attribution android:tag="OfflineLocationTimeZoneProvider"
+ android:label="@string/offline_location_time_zone_detection_service_attribution"/>
<application android:process="system"
android:persistent="true"
diff --git a/core/res/res/drawable/view_accessibility_focused.xml b/core/res/res/drawable/view_accessibility_focused.xml
index 025916b35f40..aa3031e5cf26 100644
--- a/core/res/res/drawable/view_accessibility_focused.xml
+++ b/core/res/res/drawable/view_accessibility_focused.xml
@@ -17,8 +17,8 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke
- android:width="4dp"
- android:color="@color/accessibility_focus_highlight" />
+ android:width="@dimen/accessibility_focus_highlight_stroke_width"
+ android:color="@color/accessibility_focus_highlight_color" />
<corners android:radius="2dp" />
diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml
index c71e8863502c..60507eddba22 100644
--- a/core/res/res/layout/notification_top_line_views.xml
+++ b/core/res/res/layout/notification_top_line_views.xml
@@ -88,7 +88,7 @@
<DateTimeView
android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_header_separating_margin"
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 0079d8cd0276..110e77a8aa05 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -159,7 +159,7 @@
<color name="keyguard_avatar_nick_color">#ffffffff</color>
<color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
- <color name="accessibility_focus_highlight">#bf39b500</color>
+ <color name="accessibility_focus_highlight_color">#bf39b500</color>
<color name="autofilled_highlight">#4dffeb3b</color>
<color name="system_notification_accent_color">#ff607D8B</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 37c3adbe5fcc..40e11cb92d3e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1613,21 +1613,28 @@
<!-- Whether the geolocation time zone detection feature is enabled. -->
<bool name="config_enableGeolocationTimeZoneDetection" translatable="false">true</bool>
- <!-- Whether to enable primary location time zone provider overlay which allows the primary
- location time zone provider to be replaced by an app at run-time. When disabled, only the
+ <!-- Whether the primary LocationTimeZoneProvider is enabled device.
+ Ignored if config_enableGeolocationTimeZoneDetection is false -->
+ <bool name="config_enablePrimaryLocationTimeZoneProvider" translatable="false">false</bool>
+ <!-- Used when enablePrimaryLocationTimeZoneProvider is true. Controls whether to enable primary
+ location time zone provider overlay which allows the primary location time zone provider to
+ be replaced by an app at run-time. When disabled, only the
config_primaryLocationTimeZoneProviderPackageName package will be searched for the primary
location time zone provider, otherwise any system package is eligible. Anyone who wants to
- disable the overlay mechanism can set it to false. -->
+ disable the runtime overlay mechanism can set it to false. -->
<bool name="config_enablePrimaryLocationTimeZoneOverlay" translatable="false">false</bool>
<!-- Package name providing the primary location time zone provider. Used only when
config_enablePrimaryLocationTimeZoneOverlay is false. -->
<string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
- <!-- Whether to enable secondary location time zone provider overlay which allows the secondary
- location time zone provider to be replaced by an app at run-time. When disabled, only the
- config_secondaryLocationTimeZoneProviderPackageName package will be searched for the
- secondary location time zone provider, otherwise any system package is eligible. Anyone who
- wants to disable the overlay mechanism can set it to false. -->
+ <!-- Whether the secondary LocationTimeZoneProvider is enabled device.
+ Ignored if config_enableGeolocationTimeZoneDetection is false -->
+ <bool name="config_enableSecondaryLocationTimeZoneProvider" translatable="false">true</bool>
+ <!-- Used when enableSecondaryLocationTimeZoneProvider is true. Controls whether to enable
+ secondary location time zone provider overlay which allows the primary location time zone
+ provider to config_secondaryLocationTimeZoneProviderPackageName package will be searched
+ for the secondary location time zone provider, otherwise any system package is eligible.
+ Anyone who wants to disable the runtime overlay mechanism can set it to false. -->
<bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool>
<!-- Package name providing the secondary location time zone provider. Used only when
config_enableSecondaryLocationTimeZoneOverlay is false.
@@ -4515,7 +4522,7 @@
<integer name="config_pdp_reject_retry_delay_ms">-1</integer>
<!-- Whether or not to enable the binder heavy hitter watcher by default -->
- <bool name="config_defaultBinderHeavyHitterWatcherEnabled">true</bool>
+ <bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool>
<!-- The default batch size for the binder heavy hitter watcher -->
<integer name="config_defaultBinderHeavyHitterWatcherBatchSize">2000</integer>
@@ -4526,7 +4533,7 @@
</item>
<!-- Whether or not to enable the binder heavy hitter auto sampler by default -->
- <bool name="config_defaultBinderHeavyHitterAutoSamplerEnabled">true</bool>
+ <bool name="config_defaultBinderHeavyHitterAutoSamplerEnabled">false</bool>
<!-- The default batch size for the binder heavy hitter auto sampler -->
<integer name="config_defaultBinderHeavyHitterAutoSamplerBatchSize">400</integer>
@@ -4562,4 +4569,7 @@
<!-- Indicates that default fitness tracker app needs to request sensor and location permissions. -->
<bool name="config_trackerAppNeedsPermissions">false</bool>
+
+ <!-- Component with platform query permissions for AppSearch -->
+ <string name="config_defaultAppSearchPlatformQuerierComponent" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4bcabff109ea..05db741643e8 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -553,6 +553,9 @@
<!-- Width of the outline stroke used by the accessibility screen magnification indicator -->
<dimen name="accessibility_magnification_indicator_width">4dip</dimen>
+ <!-- Width of the outline stroke used by the accessibility focus rectangle -->
+ <dimen name="accessibility_focus_highlight_stroke_width">4dp</dimen>
+
<!-- Margin around the various security views -->
<dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8e3a0cbdd10c..89b986b8fcb8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -433,9 +433,12 @@
<string name="sensor_notification_service">Sensor Notification Service</string>
<!-- Attribution for Twilight service. [CHAR LIMIT=NONE]-->
<string name="twilight_service">Twilight Service</string>
- <!-- Attribution for Offline LocationTimeZoneDetector service, i.e. one capable of performing
- time zone lookup using geo-spacial information held on the device. [CHAR LIMIT=NONE]-->
- <string name="offline_location_time_zone_detection_service">Offline Time Zone Detection Service</string>
+ <!-- Attribution for the Offline LocationTimeZoneProvider service, i.e. the service capable of
+ performing time zone detection using time zone geospatial information held on the device.
+ This text is shown in UIs related to an application name to help users and developers to
+ understand which sub-unit of an application is requesting permissions and using power.
+ [CHAR LIMIT=NONE]-->
+ <string name="offline_location_time_zone_detection_service_attribution">Time Zone Detector (No connectivity)</string>
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e96439250696..e50eee6afa91 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2168,8 +2168,10 @@
<java-symbol type="string" name="config_deviceConfiguratorPackageName" />
<java-symbol type="array" name="config_autoTimeSourcesPriority" />
<java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" />
+ <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" />
<java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
<java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
+ <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
<java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" />
<java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
@@ -4113,4 +4115,11 @@
<java-symbol type="bool" name="config_trackerAppNeedsPermissions"/>
+ <!-- Component with platform query permissions for AppSearch -->
+ <java-symbol type="string" name="config_defaultAppSearchPlatformQuerierComponent" />
+
+ <!-- Color used by the accessibility focus rectangle -->
+ <java-symbol type="color" name="accessibility_focus_highlight_color" />
+ <!-- Width of the outline stroke used by the accessibility focus rectangle -->
+ <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" />
</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
new file mode 100644
index 000000000000..e03cea31f077
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import org.junit.Test;
+
+public class SetSchemaRequestTest {
+
+ @Test
+ public void testInvalidSchemaReferences() {
+ IllegalArgumentException expected =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new SetSchemaRequest.Builder()
+ .setSchemaTypeVisibilityForSystemUi(false, "InvalidSchema")
+ .build());
+ assertThat(expected).hasMessageThat().contains("referenced, but were not added");
+ }
+
+ @Test
+ public void testSchemaTypeVisibilityForSystemUi_Visible() {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+ // By default, the schema is visible.
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build();
+ assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
+
+ request =
+ new SetSchemaRequest.Builder()
+ .addSchema(schema)
+ .setSchemaTypeVisibilityForSystemUi(true, "Schema")
+ .build();
+ assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
+ }
+
+ @Test
+ public void testSchemaTypeVisibilityForSystemUi_NotVisible() {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchema(schema)
+ .setSchemaTypeVisibilityForSystemUi(false, "Schema")
+ .build();
+ assertThat(request.getSchemasNotPlatformSurfaceable()).containsExactly("Schema");
+ }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java
index 2eaebd6e7b93..7072a8161a87 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java
@@ -78,4 +78,125 @@ public class AppSearchSchemaCtsTest {
.build()));
assertThat(e).hasMessageThat().contains("Property defined more than once: subject");
}
+
+ @Test
+ public void testEquals_identical() {
+ AppSearchSchema schema1 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ AppSearchSchema schema2 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ assertThat(schema1).isEqualTo(schema2);
+ assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode());
+ }
+
+ @Test
+ public void testEquals_differentOrder() {
+ AppSearchSchema schema1 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ AppSearchSchema schema2 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .build();
+ assertThat(schema1).isEqualTo(schema2);
+ assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode());
+ }
+
+ @Test
+ public void testEquals_failure() {
+ AppSearchSchema schema1 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ AppSearchSchema schema2 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ PropertyConfig
+ .INDEXING_TYPE_EXACT_TERMS) // Different
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ assertThat(schema1).isNotEqualTo(schema2);
+ assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode());
+ }
+
+ @Test
+ public void testEquals_failure_differentOrder() {
+ AppSearchSchema schema1 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .addProperty(
+ new PropertyConfig.Builder("body")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ // Order of 'body' and 'subject' has been switched
+ AppSearchSchema schema2 =
+ new AppSearchSchema.Builder("Email")
+ .addProperty(
+ new PropertyConfig.Builder("body")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .addProperty(
+ new PropertyConfig.Builder("subject")
+ .setDataType(PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ assertThat(schema1).isNotEqualTo(schema2);
+ assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode());
+ }
}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
new file mode 100644
index 000000000000..cfcfcc8cf044
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.util.Size;
+import android.util.SizeF;
+import android.util.SparseArray;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.UUID;
+
+public class BundleUtilTest {
+ @Test
+ public void testDeepEquals_self() {
+ Bundle one = new Bundle();
+ one.putString("a", "a");
+ assertThat(BundleUtil.deepEquals(one, one)).isTrue();
+ }
+
+ @Test
+ public void testDeepEquals_simple() {
+ Bundle one = new Bundle();
+ one.putString("a", "a");
+
+ Bundle two = new Bundle();
+ two.putString("a", "a");
+
+ assertThat(one).isNotEqualTo(two);
+ assertThat(BundleUtil.deepEquals(one, two)).isTrue();
+ }
+
+ @Test
+ public void testDeepEquals_keyMismatch() {
+ Bundle one = new Bundle();
+ one.putString("a", "a");
+
+ Bundle two = new Bundle();
+ two.putString("a", "a");
+ two.putString("b", "b");
+ assertThat(BundleUtil.deepEquals(one, two)).isFalse();
+ }
+
+ @Test
+ public void testDeepEquals_thorough_equal() {
+ Bundle[] inputs = new Bundle[2];
+ for (int i = 0; i < 2; i++) {
+ inputs[i] = createThoroughBundle();
+ }
+ assertThat(inputs[0]).isNotEqualTo(inputs[1]);
+ assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isTrue();
+ }
+
+ @Test
+ public void testDeepEquals_thorough_notEqual() {
+ Bundle[] inputs = new Bundle[2];
+ for (int i = 0; i < 2; i++) {
+ Bundle b = createThoroughBundle();
+ // Create a difference
+ assertThat(b.containsKey("doubleArray")).isTrue();
+ b.putDoubleArray("doubleArray", new double[] {18., i});
+ inputs[i] = b;
+ }
+ assertThat(inputs[0]).isNotEqualTo(inputs[1]);
+ assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isFalse();
+ }
+
+ @Test
+ public void testDeepEquals_nestedNotEquals() {
+ Bundle one = new Bundle();
+ one.putString("a", "a");
+ Bundle two = new Bundle();
+ two.putBundle("b", one);
+ Bundle twoClone = new Bundle();
+ twoClone.putBundle("b", one);
+ Bundle three = new Bundle();
+ three.putBundle("b", two);
+
+ ArrayList<Bundle> listOne = new ArrayList<>(ImmutableList.of(one, two, three));
+ ArrayList<Bundle> listOneClone = new ArrayList<>(ImmutableList.of(one, twoClone, three));
+ ArrayList<Bundle> listTwo = new ArrayList<>(ImmutableList.of(one, three, two));
+ Bundle b1 = new Bundle();
+ b1.putParcelableArrayList("key", listOne);
+ Bundle b1Clone = new Bundle();
+ b1Clone.putParcelableArrayList("key", listOneClone);
+ Bundle b2 = new Bundle();
+ b2.putParcelableArrayList("key", listTwo);
+
+ assertThat(b1).isNotEqualTo(b1Clone);
+ assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue();
+ assertThat(BundleUtil.deepEquals(b1, b2)).isFalse();
+ assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse();
+ }
+
+ @Test
+ public void testDeepEquals_sparseArray() {
+ Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID());
+ Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID());
+ Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID());
+
+ SparseArray<Parcelable> array1 = new SparseArray<>();
+ array1.put(1, parcelable1);
+ array1.put(10, parcelable2);
+
+ SparseArray<Parcelable> array1Clone = new SparseArray<>();
+ array1Clone.put(1, parcelable1);
+ array1Clone.put(10, parcelable2);
+
+ SparseArray<Parcelable> array2 = new SparseArray<>();
+ array2.put(1, parcelable1);
+ array2.put(10, parcelable3); // Different
+
+ Bundle b1 = new Bundle();
+ b1.putSparseParcelableArray("array1", array1);
+ Bundle b1Clone = new Bundle();
+ b1Clone.putSparseParcelableArray("array1", array1Clone);
+ Bundle b2 = new Bundle();
+ b2.putSparseParcelableArray("array1", array2);
+
+ assertThat(b1).isNotEqualTo(b1Clone);
+ assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue();
+ assertThat(BundleUtil.deepEquals(b1, b2)).isFalse();
+ assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse();
+ }
+
+ @Test
+ public void testDeepHashCode_same() {
+ Bundle[] inputs = new Bundle[2];
+ for (int i = 0; i < 2; i++) {
+ inputs[i] = createThoroughBundle();
+ }
+ assertThat(BundleUtil.deepHashCode(inputs[0]))
+ .isEqualTo(BundleUtil.deepHashCode(inputs[1]));
+ }
+
+ @Test
+ public void testDeepHashCode_different() {
+ Bundle[] inputs = new Bundle[2];
+ for (int i = 0; i < 2; i++) {
+ Bundle b = createThoroughBundle();
+ // Create a difference
+ assertThat(b.containsKey("doubleArray")).isTrue();
+ b.putDoubleArray("doubleArray", new double[] {18., i});
+ inputs[i] = b;
+ }
+ assertThat(BundleUtil.deepHashCode(inputs[0]))
+ .isNotEqualTo(BundleUtil.deepHashCode(inputs[1]));
+ }
+
+ @Test
+ public void testHashCode_sparseArray() {
+ Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID());
+ Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID());
+ Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID());
+
+ SparseArray<Parcelable> array1 = new SparseArray<>();
+ array1.put(1, parcelable1);
+ array1.put(10, parcelable2);
+
+ SparseArray<Parcelable> array1Clone = new SparseArray<>();
+ array1Clone.put(1, parcelable1);
+ array1Clone.put(10, parcelable2);
+
+ SparseArray<Parcelable> array2 = new SparseArray<>();
+ array2.put(1, parcelable1);
+ array2.put(10, parcelable3); // Different
+
+ Bundle b1 = new Bundle();
+ b1.putSparseParcelableArray("array1", array1);
+ Bundle b1Clone = new Bundle();
+ b1Clone.putSparseParcelableArray("array1", array1Clone);
+ Bundle b2 = new Bundle();
+ b2.putSparseParcelableArray("array1", array2);
+
+ assertThat(b1.hashCode()).isNotEqualTo(b1Clone.hashCode());
+ assertThat(BundleUtil.deepHashCode(b1)).isEqualTo(BundleUtil.deepHashCode(b1Clone));
+ assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2));
+ }
+
+ private static Bundle createThoroughBundle() {
+ Bundle toy1 = new Bundle();
+ toy1.putString("a", "a");
+ Bundle toy2 = new Bundle();
+ toy2.putInt("b", 2);
+
+ Bundle b = new Bundle();
+ // BaseBundle stuff
+ b.putBoolean("boolean", true);
+ b.putByte("byte", (byte) 1);
+ b.putChar("char", 'a');
+ b.putShort("short", (short) 2);
+ b.putInt("int", 3);
+ b.putLong("long", 4L);
+ b.putFloat("float", 5f);
+ b.putDouble("double", 6f);
+ b.putString("string", "b");
+ b.putCharSequence("charSequence", "c");
+ b.putIntegerArrayList("integerArrayList", new ArrayList<>(ImmutableList.of(7, 8)));
+ b.putStringArrayList("stringArrayList", new ArrayList<>(ImmutableList.of("d", "e")));
+ b.putCharSequenceArrayList(
+ "charSequenceArrayList", new ArrayList<>(ImmutableList.of("f", "g")));
+ b.putSerializable("serializable", new BigDecimal(9));
+ b.putBooleanArray("booleanArray", new boolean[] {true, false, true});
+ b.putByteArray("byteArray", new byte[] {(byte) 10, (byte) 11});
+ b.putShortArray("shortArray", new short[] {(short) 12, (short) 13});
+ b.putCharArray("charArray", new char[] {'h', 'i'});
+ b.putLongArray("longArray", new long[] {14L, 15L});
+ b.putFloatArray("floatArray", new float[] {16f, 17f});
+ b.putDoubleArray("doubleArray", new double[] {18., 19.});
+ b.putStringArray("stringArray", new String[] {"j", "k"});
+ b.putCharSequenceArray("charSequenceArrayList", new CharSequence[] {"l", "m"});
+
+ // Bundle stuff
+ b.putParcelable("parcelable", toy1);
+ if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
+ b.putSize("size", new Size(20, 21));
+ b.putSizeF("sizeF", new SizeF(22f, 23f));
+ }
+ b.putParcelableArray("parcelableArray", new Parcelable[] {toy1, toy2});
+ b.putParcelableArrayList(
+ "parcelableArrayList", new ArrayList<>(ImmutableList.of(toy1, toy2)));
+ SparseArray<Parcelable> sparseArray = new SparseArray<>();
+ sparseArray.put(24, toy1);
+ sparseArray.put(1025, toy2);
+ b.putSparseParcelableArray("sparceParcelableArray", sparseArray);
+ b.putBundle("bundle", toy1);
+
+ return b;
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index 365e97ded928..e04d9034d2c1 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -24,12 +24,12 @@ import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.util.AttributeSet;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import androidx.test.filters.LargeTest;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -308,12 +308,12 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
- public void writeAsXml(TestServiceType item, XmlSerializer out) throws IOException {
+ public void writeAsXml(TestServiceType item, TypedXmlSerializer out) throws IOException {
out.attribute(null, "type", item.type);
out.attribute(null, "value", item.value);
}
- public TestServiceType createFromXml(XmlPullParser parser)
+ public TestServiceType createFromXml(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
final String type = parser.getAttributeValue(null, "type");
final String value = parser.getAttributeValue(null, "value");
diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
index 4370462279b2..e750454a01ff 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -23,18 +23,16 @@ import static org.junit.Assert.fail;
import android.os.Parcel;
import android.util.Pair;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.FastXmlSerializer;
-
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -163,7 +161,7 @@ public class BrightnessConfigurationTest {
BrightnessConfiguration config = builder.build();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- XmlSerializer out = new FastXmlSerializer();
+ TypedXmlSerializer out = Xml.newFastSerializer();
out.setOutput(baos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
config.saveToXml(out);
@@ -171,7 +169,7 @@ public class BrightnessConfigurationTest {
baos.flush();
ByteArrayInputStream input = new ByteArrayInputStream(baos.toByteArray());
- XmlPullParser parser = Xml.newPullParser();
+ TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(input, StandardCharsets.UTF_8.name());
BrightnessConfiguration loadedConfig = BrightnessConfiguration.loadFromXml(parser);
diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java
index be63a0ecc65f..6fa5a2343361 100644
--- a/core/tests/coretests/src/android/util/BinaryXmlTest.java
+++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java
@@ -20,6 +20,8 @@ import static android.util.XmlTest.assertNext;
import static android.util.XmlTest.buildPersistableBundle;
import static android.util.XmlTest.doPersistableBundleRead;
import static android.util.XmlTest.doPersistableBundleWrite;
+import static android.util.XmlTest.doVerifyRead;
+import static android.util.XmlTest.doVerifyWrite;
import static org.junit.Assert.assertEquals;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -33,6 +35,11 @@ import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
@RunWith(AndroidJUnit4.class)
@@ -96,4 +103,58 @@ public class BinaryXmlTest {
final PersistableBundle actual = doPersistableBundleRead(secondIn, os.toByteArray());
assertEquals(expected.toString(), actual.toString());
}
+
+ @Test
+ public void testResolve_File() throws Exception {
+ {
+ final File file = File.createTempFile("fast", ".xml");
+ try (OutputStream os = new FileOutputStream(file)) {
+ TypedXmlSerializer xml = Xml.newFastSerializer();
+ xml.setOutput(os, StandardCharsets.UTF_8.name());
+ doVerifyWrite(xml);
+ }
+ try (InputStream is = new FileInputStream(file)) {
+ doVerifyRead(Xml.resolvePullParser(is));
+ }
+ }
+ {
+ final File file = File.createTempFile("binary", ".xml");
+ try (OutputStream os = new FileOutputStream(file)) {
+ TypedXmlSerializer xml = Xml.newBinarySerializer();
+ xml.setOutput(os, StandardCharsets.UTF_8.name());
+ doVerifyWrite(xml);
+ }
+ try (InputStream is = new FileInputStream(file)) {
+ doVerifyRead(Xml.resolvePullParser(is));
+ }
+ }
+ }
+
+ @Test
+ public void testResolve_Memory() throws Exception {
+ {
+ final byte[] data;
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ TypedXmlSerializer xml = Xml.newFastSerializer();
+ xml.setOutput(os, StandardCharsets.UTF_8.name());
+ doVerifyWrite(xml);
+ data = os.toByteArray();
+ }
+ try (InputStream is = new ByteArrayInputStream(data)) {
+ doVerifyRead(Xml.resolvePullParser(is));
+ }
+ }
+ {
+ final byte[] data;
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ TypedXmlSerializer xml = Xml.newBinarySerializer();
+ xml.setOutput(os, StandardCharsets.UTF_8.name());
+ doVerifyWrite(xml);
+ data = os.toByteArray();
+ }
+ try (InputStream is = new ByteArrayInputStream(data)) {
+ doVerifyRead(Xml.resolvePullParser(is));
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/util/XmlTest.java b/core/tests/coretests/src/android/util/XmlTest.java
index 5ae17c46944f..a8fc6f933605 100644
--- a/core/tests/coretests/src/android/util/XmlTest.java
+++ b/core/tests/coretests/src/android/util/XmlTest.java
@@ -205,7 +205,7 @@ public class XmlTest {
private static final byte[] TEST_BYTES = new byte[] { 0, 1, 2, 3, 4, 3, 2, 1, 0 };
private static final byte[] TEST_BYTES_EMPTY = new byte[0];
- private static void doVerifyWrite(TypedXmlSerializer out) throws Exception {
+ static void doVerifyWrite(TypedXmlSerializer out) throws Exception {
out.startDocument(StandardCharsets.UTF_8.name(), true);
out.startTag(null, "one");
{
@@ -244,7 +244,7 @@ public class XmlTest {
out.endDocument();
}
- private static void doVerifyRead(TypedXmlPullParser in) throws Exception {
+ static void doVerifyRead(TypedXmlPullParser in) throws Exception {
assertEquals(START_DOCUMENT, in.getEventType());
assertNext(in, START_TAG, "one", 1);
{
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 8e2490789a6f..75116d8c6df2 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -38,6 +38,7 @@ import android.os.UserHandle;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.internal.util.IntPair;
import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -73,12 +74,18 @@ public class AccessibilityManagerTest {
@Mock private IAccessibilityManager mMockService;
private MessageCapturingHandler mHandler;
private Instrumentation mInstrumentation;
+ private int mFocusStrokeWidthDefaultValue;
+ private int mFocusColorDefaultValue;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mHandler = new MessageCapturingHandler(null);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mFocusStrokeWidthDefaultValue = mInstrumentation.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width);
+ mFocusColorDefaultValue = mInstrumentation.getContext().getResources().getColor(
+ R.color.accessibility_focus_highlight_color);
}
@After
@@ -94,8 +101,12 @@ public class AccessibilityManagerTest {
when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
.thenReturn(serviceReturnValue);
+ when(mMockService.getFocusStrokeWidth()).thenReturn(mFocusStrokeWidthDefaultValue);
+ when(mMockService.getFocusColor()).thenReturn(mFocusColorDefaultValue);
+
AccessibilityManager manager =
- new AccessibilityManager(mHandler, mMockService, UserHandle.USER_CURRENT);
+ new AccessibilityManager(mInstrumentation.getContext(), mHandler, mMockService,
+ UserHandle.USER_CURRENT, true);
verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt());
mHandler.setCallback(manager.getCallback());
@@ -205,4 +216,16 @@ public class AccessibilityManagerTest {
verify(mMockService).setWindowMagnificationConnection(connection);
}
+
+ @Test
+ public void testGetDefaultValueOfFocusAppearanceData() {
+ AccessibilityManager manager =
+ new AccessibilityManager(mInstrumentation.getContext(), mHandler, null,
+ UserHandle.USER_CURRENT, false);
+
+ assertEquals(mFocusStrokeWidthDefaultValue,
+ manager.getAccessibilityFocusStrokeWidth());
+ assertEquals(mFocusColorDefaultValue,
+ manager.getAccessibilityFocusColor());
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 75a7504cac2f..cfdb2b769a08 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -161,4 +161,6 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {}
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {}
+
+ public void setFocusAppearance(int strokeWidth, int color) {}
}
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 c17c36eba2dc..6baf3056be32 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -68,6 +68,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.widget.Toast;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
@@ -148,7 +149,8 @@ public class AccessibilityShortcutControllerTest {
// Use the extra level of indirection in the object to mock framework objects
AccessibilityManager accessibilityManager =
- new AccessibilityManager(mHandler, mAccessibilityManagerService, 0);
+ new AccessibilityManager(InstrumentationRegistry.getContext(), mHandler,
+ mAccessibilityManagerService, 0, true);
when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext))
.thenReturn(accessibilityManager);
when(mContext.getSystemService(Context.ACCESSIBILITY_SERVICE))
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 46695d2764dd..a74f580b65e6 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -38,13 +38,11 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.Activity;
-import android.app.ActivityTaskManager;
+import android.app.ActivityClient;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
-import android.app.IActivityTaskManager;
import android.app.LoadedApk;
import android.app.servertransaction.PendingTransactionActions;
import android.content.ComponentName;
@@ -54,7 +52,6 @@ import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.testing.PollingCheck;
@@ -224,18 +221,19 @@ public class ActivityThreadClientTest {
private MockitoSession mMockSession;
private ActivityThread mThread;
- private ClientMockSession() throws RemoteException {
+ private ClientMockSession() {
mThread = ActivityThread.currentActivityThread();
mMockSession = mockitoSession()
.strictness(Strictness.LENIENT)
- .spyStatic(ActivityTaskManager.class)
+ .spyStatic(ActivityClient.class)
.spyStatic(WindowManagerGlobal.class)
.startMocking();
doReturn(Mockito.mock(WindowManagerGlobal.class))
.when(WindowManagerGlobal::getInstance);
- IActivityTaskManager mockAtm = Mockito.mock(IActivityTaskManager.class);
- doReturn(mockAtm).when(ActivityTaskManager::getService);
- when(mockAtm.finishActivity(any(), anyInt(), any(), anyInt())).thenReturn(true);
+ final ActivityClient mockAc = Mockito.mock(ActivityClient.class);
+ doReturn(mockAc).when(ActivityClient::getInstance);
+ doReturn(true).when(mockAc).finishActivity(any() /* token */,
+ anyInt() /* resultCode */, any() /* resultData */, anyInt() /* finishTask */);
}
private Activity launchActivity(ActivityClientRecord r) {
diff --git a/core/tests/overlaytests/remount/TEST_MAPPING b/core/tests/overlaytests/remount/TEST_MAPPING
index 54dd4310bd6e..22b28b5344bb 100644
--- a/core/tests/overlaytests/remount/TEST_MAPPING
+++ b/core/tests/overlaytests/remount/TEST_MAPPING
@@ -1,7 +1,7 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name" : "OverlayRemountedTest"
}
]
-} \ No newline at end of file
+}
diff --git a/core/tests/uwbtests/Android.bp b/core/tests/uwbtests/Android.bp
index c41c346b131a..8ee86f470c9e 100644
--- a/core/tests/uwbtests/Android.bp
+++ b/core/tests/uwbtests/Android.bp
@@ -17,6 +17,7 @@ android_test {
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
+ "mockito-target-minus-junit4",
],
libs: [
"android.test.runner",
diff --git a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java
new file mode 100644
index 000000000000..ce67ef7868e8
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
+import android.os.RemoteException;
+import android.uwb.UwbManager.AdapterStateCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Test of {@link AdapterStateListener}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AdapterStateListenerTest {
+
+ IUwbAdapter mUwbAdapter = mock(IUwbAdapter.class);
+
+ Answer mRegisterSuccessAnswer = new Answer() {
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
+ try {
+ cb.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
+ } catch (RemoteException e) {
+ // Nothing to do
+ }
+ return new Object();
+ }
+ };
+
+ Throwable mThrowRemoteException = new RemoteException("RemoteException");
+
+ private static Executor getExecutor() {
+ return new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+ }
+
+ private static void verifyCallbackStateChangedInvoked(
+ AdapterStateCallback callback, int numTimes) {
+ verify(callback, times(numTimes)).onStateChanged(anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void testRegister_RegisterUnregister() throws RemoteException {
+ doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
+ AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
+
+ // Verify that the adapter state listener registered with the UWB Adapter
+ adapterStateListener.register(getExecutor(), callback1);
+ verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
+ verifyCallbackStateChangedInvoked(callback1, 1);
+ verifyCallbackStateChangedInvoked(callback2, 0);
+
+ // Register a second client and no new call to UWB Adapter
+ adapterStateListener.register(getExecutor(), callback2);
+ verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
+ verifyCallbackStateChangedInvoked(callback1, 1);
+ verifyCallbackStateChangedInvoked(callback2, 1);
+
+ // Unregister first callback
+ adapterStateListener.unregister(callback1);
+ verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
+ verify(mUwbAdapter, times(0)).unregisterAdapterStateCallbacks(any());
+ verifyCallbackStateChangedInvoked(callback1, 1);
+ verifyCallbackStateChangedInvoked(callback2, 1);
+
+ // Unregister second callback
+ adapterStateListener.unregister(callback2);
+ verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
+ verify(mUwbAdapter, times(1)).unregisterAdapterStateCallbacks(any());
+ verifyCallbackStateChangedInvoked(callback1, 1);
+ verifyCallbackStateChangedInvoked(callback2, 1);
+ }
+
+ @Test
+ public void testRegister_FirstRegisterFails() throws RemoteException {
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
+ AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
+
+ // Throw a remote exception whenever first registering
+ doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+
+ adapterStateListener.register(getExecutor(), callback1);
+ verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
+
+ // No longer throw an exception, instead succeed
+ doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+
+ // Register a different callback
+ adapterStateListener.register(getExecutor(), callback2);
+ verify(mUwbAdapter, times(2)).registerAdapterStateCallbacks(any());
+
+ // Ensure first callback was invoked again
+ verifyCallbackStateChangedInvoked(callback1, 2);
+ verifyCallbackStateChangedInvoked(callback2, 1);
+ }
+
+ @Test
+ public void testRegister_RegisterSameCallbackTwice() throws RemoteException {
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ AdapterStateCallback callback = mock(AdapterStateCallback.class);
+ doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+
+ adapterStateListener.register(getExecutor(), callback);
+ verifyCallbackStateChangedInvoked(callback, 1);
+
+ adapterStateListener.register(getExecutor(), callback);
+ verifyCallbackStateChangedInvoked(callback, 1);
+
+ // Invoke a state change and ensure the callback is only called once
+ adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
+ verifyCallbackStateChangedInvoked(callback, 2);
+ }
+
+ @Test
+ public void testCallback_RunViaExecutor_Success() throws RemoteException {
+ // Verify that the callbacks are invoked on the executor when successful
+ doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+ runViaExecutor();
+ }
+
+ @Test
+ public void testCallback_RunViaExecutor_Failure() throws RemoteException {
+ // Verify that the callbacks are invoked on the executor when there is a remote exception
+ doThrow(mThrowRemoteException).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+ runViaExecutor();
+ }
+
+ private void runViaExecutor() {
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ AdapterStateCallback callback = mock(AdapterStateCallback.class);
+
+ Executor executor = mock(Executor.class);
+
+ // Do not run commands received and ensure that the callback is not invoked
+ doAnswer(new ExecutorAnswer(false)).when(executor).execute(any());
+ adapterStateListener.register(executor, callback);
+ verify(executor, times(1)).execute(any());
+ verifyCallbackStateChangedInvoked(callback, 0);
+
+ // Manually invoke the callback and ensure callback is not invoked
+ adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
+ verify(executor, times(2)).execute(any());
+ verifyCallbackStateChangedInvoked(callback, 0);
+
+ // Run the command that the executor receives
+ doAnswer(new ExecutorAnswer(true)).when(executor).execute(any());
+ adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
+ verify(executor, times(3)).execute(any());
+ verifyCallbackStateChangedInvoked(callback, 1);
+ }
+
+ class ExecutorAnswer implements Answer {
+
+ final boolean mShouldRun;
+ ExecutorAnswer(boolean shouldRun) {
+ mShouldRun = shouldRun;
+ }
+
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ if (mShouldRun) {
+ ((Runnable) invocation.getArgument(0)).run();
+ }
+ return null;
+ }
+ }
+
+ @Test
+ public void testNotify_AllCallbacksNotified() throws RemoteException {
+ doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ List<AdapterStateCallback> callbacks = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ AdapterStateCallback callback = mock(AdapterStateCallback.class);
+ adapterStateListener.register(getExecutor(), callback);
+ callbacks.add(callback);
+ }
+
+ // Ensure every callback got the initial state
+ for (AdapterStateCallback callback : callbacks) {
+ verifyCallbackStateChangedInvoked(callback, 1);
+ }
+
+ // Invoke a state change and ensure all callbacks are invoked
+ adapterStateListener.onAdapterStateChanged(true, StateChangeReason.ALL_SESSIONS_CLOSED);
+ for (AdapterStateCallback callback : callbacks) {
+ verifyCallbackStateChangedInvoked(callback, 2);
+ }
+ }
+
+ @Test
+ public void testStateChange_CorrectValue() {
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+
+ AdapterStateCallback callback = mock(AdapterStateCallback.class);
+
+ adapterStateListener.register(getExecutor(), callback);
+
+ runStateChangeValue(StateChangeReason.ALL_SESSIONS_CLOSED,
+ AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED);
+
+ runStateChangeValue(StateChangeReason.SESSION_STARTED,
+ AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED);
+
+ runStateChangeValue(StateChangeReason.SYSTEM_BOOT,
+ AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT);
+
+ runStateChangeValue(StateChangeReason.SYSTEM_POLICY,
+ AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY);
+
+ runStateChangeValue(StateChangeReason.UNKNOWN,
+ AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN);
+ }
+
+ private void runStateChangeValue(@StateChangeReason int reasonIn,
+ @AdapterStateCallback.StateChangedReason int reasonOut) {
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ AdapterStateCallback callback = mock(AdapterStateCallback.class);
+ adapterStateListener.register(getExecutor(), callback);
+
+ adapterStateListener.onAdapterStateChanged(false, reasonIn);
+ verify(callback, times(1)).onStateChanged(false, reasonOut);
+
+ adapterStateListener.onAdapterStateChanged(true, reasonIn);
+ verify(callback, times(1)).onStateChanged(true, reasonOut);
+ }
+
+ @Test
+ public void testStateChange_FirstRegisterGetsCorrectState() throws RemoteException {
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ AdapterStateCallback callback = mock(AdapterStateCallback.class);
+
+ Answer registerAnswer = new Answer() {
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
+ try {
+ cb.onAdapterStateChanged(true, StateChangeReason.SESSION_STARTED);
+ } catch (RemoteException e) {
+ // Nothing to do
+ }
+ return new Object();
+ }
+ };
+
+ doAnswer(registerAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
+
+ adapterStateListener.register(getExecutor(), callback);
+ verify(callback).onStateChanged(true,
+ AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED);
+ }
+
+ @Test
+ public void testStateChange_SecondRegisterGetsCorrectState() {
+ AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
+ AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
+ AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
+
+ adapterStateListener.register(getExecutor(), callback1);
+ adapterStateListener.onAdapterStateChanged(true, StateChangeReason.SYSTEM_BOOT);
+
+ adapterStateListener.register(getExecutor(), callback2);
+ verify(callback2).onStateChanged(true,
+ AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT);
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java b/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java
new file mode 100644
index 000000000000..4983bed742fd
--- /dev/null
+++ b/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uwb;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of {@link UwbManager}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UwbManagerTest {
+
+ public final Context mContext = InstrumentationRegistry.getContext();
+
+ @Test
+ public void testServiceAvailable() {
+ UwbManager manager = mContext.getSystemService(UwbManager.class);
+ if (UwbTestUtils.isUwbSupported(mContext)) {
+ assertNotNull(manager);
+ } else {
+ assertNull(manager);
+ }
+ }
+}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
index 62e0b629539b..fb7509248b38 100644
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -16,6 +16,8 @@
package android.uwb;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.SystemClock;
import java.util.ArrayList;
@@ -24,6 +26,11 @@ import java.util.List;
public class UwbTestUtils {
private UwbTestUtils() {}
+ public static boolean isUwbSupported(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB);
+ }
+
public static AngleMeasurement getAngleMeasurement() {
return new AngleMeasurement.Builder()
.setRadians(getDoubleInRange(-Math.PI, Math.PI))
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 1fb63f04ccf5..5f129fe03b61 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -243,6 +243,8 @@ applications that come with the platform
<permission name="android.permission.LOG_COMPAT_CHANGE" />
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<permission name="android.permission.REGISTER_STATS_PULL_ATOM" />
+ <!-- Permissions required for reading DeviceConfig -->
+ <permission name="android.permission.READ_DEVICE_CONFIG" />
</privapp-permissions>
<privapp-permissions package="com.android.providers.telephony">
@@ -375,6 +377,7 @@ applications that come with the platform
<permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES"/>
<permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
+ <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
<permission name="android.permission.START_TASKS_FROM_RECENTS" />
<permission name="android.permission.STOP_APP_SWITCHES"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
@@ -445,6 +448,8 @@ applications that come with the platform
<!-- Permissions required for CTS test - android.server.biometrics -->
<permission name="android.permission.USE_BIOMETRIC" />
<permission name="android.permission.TEST_BIOMETRIC" />
+ <!-- Permissions required for CTS test - CtsContactsProviderTestCases -->
+ <permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6bcab8a34e2c..a5667b2f0a8a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -85,6 +85,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-2014162875": {
+ "message": "Could not register window container listener token=%s, container=%s",
+ "level": "ERROR",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
+ },
"-2012562539": {
"message": "startAnimation(): Notify animation start:",
"level": "DEBUG",
@@ -799,12 +805,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
- "-1155279885": {
- "message": "Frontmost changed immersion: %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_IMMERSIVE",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"-1144293044": {
"message": "SURFACE SET FREEZE LAYER: %s",
"level": "INFO",
@@ -817,6 +817,12 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-1136467585": {
+ "message": "The listener does not exist.",
+ "level": "INFO",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
+ },
"-1136139407": {
"message": "no-history finish of %s",
"level": "DEBUG",
@@ -1219,6 +1225,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-655104359": {
+ "message": "Frontmost changed immersion: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_IMMERSIVE",
+ "at": "com\/android\/server\/wm\/ActivityClientController.java"
+ },
"-653156702": {
"message": "createAppAnimations()",
"level": "DEBUG",
@@ -1573,12 +1585,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
- "-272719931": {
- "message": "startLockTaskModeLocked: %s",
- "level": "WARN",
- "group": "WM_DEBUG_LOCKTASK",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"-262984451": {
"message": "Relaunch failed %s",
"level": "INFO",
@@ -1825,6 +1831,18 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "90764070": {
+ "message": "Could not report token removal to the window token client.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
+ },
+ "91350919": {
+ "message": "Attempted to set IME flag to a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"94402792": {
"message": "Moving to RESUMED: %s (in existing)",
"level": "VERBOSE",
@@ -1969,6 +1987,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
+ "236210101": {
+ "message": "registerWindowContextListener: trying to add listener to a non-existing display:%d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"241961619": {
"message": "Adding %s to %s",
"level": "VERBOSE",
@@ -2047,6 +2071,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
+ "295861935": {
+ "message": "startLockTaskMode: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_LOCKTASK",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"302969511": {
"message": "Task info changed taskId=%d",
"level": "VERBOSE",
@@ -2557,6 +2587,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/TaskDisplayArea.java"
},
+ "883475718": {
+ "message": "Report configuration: %s %s %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/ActivityClientController.java"
+ },
"892244061": {
"message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d",
"level": "INFO",
@@ -3097,12 +3133,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1576607724": {
- "message": "Report configuration: %s %s %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"1577579529": {
"message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b",
"level": "ERROR",
@@ -3403,6 +3433,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "1948483534": {
+ "message": "Could not report config changes to the window token client.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowContextListenerController.java"
+ },
"1964565370": {
"message": "Starting remote animation",
"level": "INFO",
diff --git a/errorprone/refaster/EfficientXml.java b/errorprone/refaster/EfficientXml.java
index bd1ddfc92e91..ae797c46b77e 100644
--- a/errorprone/refaster/EfficientXml.java
+++ b/errorprone/refaster/EfficientXml.java
@@ -292,6 +292,30 @@ public class EfficientXml {
}
}
+ class BooleanToStringTrue {
+ @BeforeTemplate
+ void before(TypedXmlSerializer out, String n) throws Exception {
+ out.attribute(null, n, "true");
+ }
+
+ @AfterTemplate
+ void after(TypedXmlSerializer out, String n) throws Exception {
+ out.attributeBoolean(null, n, true);
+ }
+ }
+
+ class BooleanToStringFalse {
+ @BeforeTemplate
+ void before(TypedXmlSerializer out, String n) throws Exception {
+ out.attribute(null, n, "false");
+ }
+
+ @AfterTemplate
+ void after(TypedXmlSerializer out, String n) throws Exception {
+ out.attributeBoolean(null, n, false);
+ }
+ }
+
class BooleanFromString {
@BeforeTemplate
boolean beforeParse(TypedXmlPullParser in, String n) throws Exception {
diff --git a/errorprone/refaster/EfficientXml.java.refaster b/errorprone/refaster/EfficientXml.java.refaster
index f2974fffb83b..750c2dbe98c0 100644
--- a/errorprone/refaster/EfficientXml.java.refaster
+++ b/errorprone/refaster/EfficientXml.java.refaster
Binary files differ
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 0061ea13f647..d59abb5916a0 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -43,7 +43,7 @@ public final class FrameInfo {
public long[] frameInfo = new long[FRAME_INFO_SIZE];
// Various flags set to provide extra metadata about the current frame
- private static final int FLAGS = 0;
+ public static final int FLAGS = 0;
// Is this the first-draw following a window layout?
public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1;
@@ -60,35 +60,35 @@ public final class FrameInfo {
@Retention(RetentionPolicy.SOURCE)
public @interface FrameInfoFlags {}
- private static final int FRAME_TIMELINE_VSYNC_ID = 1;
+ public static final int FRAME_TIMELINE_VSYNC_ID = 1;
// The intended vsync time, unadjusted by jitter
- private static final int INTENDED_VSYNC = 2;
+ public static final int INTENDED_VSYNC = 2;
// Jitter-adjusted vsync time, this is what was used as input into the
// animation & drawing system
- private static final int VSYNC = 3;
+ public static final int VSYNC = 3;
// The time of the oldest input event
- private static final int OLDEST_INPUT_EVENT = 4;
+ public static final int OLDEST_INPUT_EVENT = 4;
// The time of the newest input event
- private static final int NEWEST_INPUT_EVENT = 5;
+ public static final int NEWEST_INPUT_EVENT = 5;
// When input event handling started
- private static final int HANDLE_INPUT_START = 6;
+ public static final int HANDLE_INPUT_START = 6;
// When animation evaluations started
- private static final int ANIMATION_START = 7;
+ public static final int ANIMATION_START = 7;
// When ViewRootImpl#performTraversals() started
- private static final int PERFORM_TRAVERSALS_START = 8;
+ public static final int PERFORM_TRAVERSALS_START = 8;
// When View:draw() started
- private static final int DRAW_START = 9;
+ public static final int DRAW_START = 9;
// When the frame needs to be ready by
- private static final int FRAME_DEADLINE = 10;
+ public static final int FRAME_DEADLINE = 10;
// Must be the last one
private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1;
@@ -99,23 +99,11 @@ public final class FrameInfo {
frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
frameInfo[INTENDED_VSYNC] = intendedVsync;
frameInfo[VSYNC] = usedVsync;
- frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
- frameInfo[NEWEST_INPUT_EVENT] = 0;
frameInfo[FLAGS] = 0;
frameInfo[FRAME_DEADLINE] = frameDeadline;
}
/** checkstyle */
- public void updateInputEventTime(long inputEventTime, long inputEventOldestTime) {
- if (inputEventOldestTime < frameInfo[OLDEST_INPUT_EVENT]) {
- frameInfo[OLDEST_INPUT_EVENT] = inputEventOldestTime;
- }
- if (inputEventTime > frameInfo[NEWEST_INPUT_EVENT]) {
- frameInfo[NEWEST_INPUT_EVENT] = inputEventTime;
- }
- }
-
- /** checkstyle */
public void markInputHandlingStart() {
frameInfo[HANDLE_INPUT_START] = System.nanoTime();
}
@@ -131,13 +119,7 @@ public final class FrameInfo {
}
/** checkstyle */
- public void markDrawStart() {
- frameInfo[DRAW_START] = System.nanoTime();
- }
-
- /** checkstyle */
public void addFlags(@FrameInfoFlags long flags) {
frameInfo[FLAGS] |= flags;
}
-
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 24987daf87b5..d6b4f1824617 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -82,7 +82,7 @@ public class Typeface {
private static String TAG = "Typeface";
/** @hide */
- public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = false;
+ public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = true;
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index e7c15821e5b3..fea756c8290f 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -124,6 +124,19 @@ public final class Font {
}
/**
+ * Construct a builder with a byte buffer and file path.
+ *
+ * This method is intended to be called only from SystemFonts.
+ * @param path font file path
+ * @param localeList comma concatenated BCP47 compliant language tag.
+ * @hide
+ */
+ public Builder(@NonNull File path, @NonNull String localeList) {
+ this(path);
+ mLocaleList = localeList;
+ }
+
+ /**
* Constructs a builder with a file path.
*
* @param path a file path to the font file
@@ -809,29 +822,13 @@ public final class Font {
// If not found, create Font object from native object for Java API users.
ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
- long packed = nGetFontInfo(ptr);
- int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
- boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
- int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
- int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
- FontVariationAxis[] axes = new FontVariationAxis[axisCount];
- char[] charBuffer = new char[4];
- for (int i = 0; i < axisCount; ++i) {
- long packedAxis = nGetAxisInfo(ptr, i);
- float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
- charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
- charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
- charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
- charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
- axes[i] = new FontVariationAxis(new String(charBuffer), value);
- }
- String path = nGetFontPath(ptr);
- File file = (path == null) ? null : new File(path);
- Font.Builder builder = new Font.Builder(buffer, file, "")
- .setWeight(weight)
- .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
- .setTtcIndex(ttcIndex)
- .setFontVariationSettings(axes);
+ NativeFont.Font font = NativeFont.readNativeFont(ptr);
+
+ Font.Builder builder = new Font.Builder(buffer, font.getFile(), "")
+ .setWeight(font.getStyle().getWeight())
+ .setSlant(font.getStyle().getSlant())
+ .setTtcIndex(font.getIndex())
+ .setFontVariationSettings(font.getAxes());
Font newFont = null;
try {
@@ -845,15 +842,6 @@ public final class Font {
}
}
- @CriticalNative
- private static native long nGetFontInfo(long ptr);
-
- @CriticalNative
- private static native long nGetAxisInfo(long ptr, int i);
-
- @FastNative
- private static native String nGetFontPath(long ptr);
-
@FastNative
private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java
new file mode 100644
index 000000000000..9e9d76a89385
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/NativeFont.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.fonts;
+
+import android.graphics.Typeface;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Read native font objects.
+ *
+ * @hide
+ */
+public class NativeFont {
+
+ /**
+ * Represents native font object.
+ */
+ public static final class Font {
+ private final File mFile;
+ private final int mIndex;
+ private final FontVariationAxis[] mAxes;
+ private final FontStyle mStyle;
+
+ public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) {
+ mFile = file;
+ mIndex = index;
+ mAxes = axes;
+ mStyle = style;
+ }
+
+ public File getFile() {
+ return mFile;
+ }
+
+ public FontVariationAxis[] getAxes() {
+ return mAxes;
+ }
+
+ public FontStyle getStyle() {
+ return mStyle;
+ }
+
+ public int getIndex() {
+ return mIndex;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Font font = (Font) o;
+ return mIndex == font.mIndex && mFile.equals(font.mFile)
+ && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mFile, mIndex, mStyle);
+ result = 31 * result + Arrays.hashCode(mAxes);
+ return result;
+ }
+ }
+
+ /**
+ * Represents native font family object.
+ */
+ public static final class Family {
+ private final List<Font> mFonts;
+ private final String mLocale;
+
+ public Family(List<Font> fonts, String locale) {
+ mFonts = fonts;
+ mLocale = locale;
+ }
+
+ public List<Font> getFonts() {
+ return mFonts;
+ }
+
+ public String getLocale() {
+ return mLocale;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Family family = (Family) o;
+ return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFonts, mLocale);
+ }
+ }
+
+ /**
+ * Get underlying font families from Typeface
+ *
+ * @param typeface a typeface
+ * @return list of family
+ */
+ public static List<Family> readTypeface(Typeface typeface) {
+ int familyCount = nGetFamilyCount(typeface.native_instance);
+ List<Family> result = new ArrayList<>(familyCount);
+ for (int i = 0; i < familyCount; ++i) {
+ result.add(readNativeFamily(nGetFamily(typeface.native_instance, i)));
+ }
+ return result;
+ }
+
+ /**
+ * Read family object from native pointer
+ *
+ * @param familyPtr a font family pointer
+ * @return a family
+ */
+ public static Family readNativeFamily(long familyPtr) {
+ int fontCount = nGetFontCount(familyPtr);
+ List<Font> result = new ArrayList<>(fontCount);
+ for (int i = 0; i < fontCount; ++i) {
+ result.add(readNativeFont(nGetFont(familyPtr, i)));
+ }
+ String localeList = nGetLocaleList(familyPtr);
+ return new Family(result, localeList);
+ }
+
+ /**
+ * Read font object from native pointer.
+ *
+ * @param ptr a font pointer
+ * @return a font
+ */
+ public static Font readNativeFont(long ptr) {
+ long packed = nGetFontInfo(ptr);
+ int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
+ boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
+ int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
+ int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
+ FontVariationAxis[] axes = new FontVariationAxis[axisCount];
+ char[] charBuffer = new char[4];
+ for (int i = 0; i < axisCount; ++i) {
+ long packedAxis = nGetAxisInfo(ptr, i);
+ float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
+ charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
+ charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
+ charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
+ charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
+ axes[i] = new FontVariationAxis(new String(charBuffer), value);
+ }
+ String path = nGetFontPath(ptr);
+ File file = (path == null) ? null : new File(path);
+ FontStyle style = new FontStyle(weight,
+ italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
+
+ return new Font(file, ttcIndex, axes, style);
+ }
+
+ @CriticalNative
+ private static native int nGetFamilyCount(long ptr);
+
+ @CriticalNative
+ private static native long nGetFamily(long ptr, int index);
+
+ @FastNative
+ private static native String nGetLocaleList(long familyPtr);
+
+ @CriticalNative
+ private static native long nGetFont(long familyPtr, int fontIndex);
+
+ @CriticalNative
+ private static native int nGetFontCount(long familyPtr);
+
+ @CriticalNative
+ private static native long nGetFontInfo(long fontPtr);
+
+ @CriticalNative
+ private static native long nGetAxisInfo(long fontPtr, int i);
+
+ @FastNative
+ private static native String nGetFontPath(long fontPtr);
+}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 3635adc3ce39..fb6ea99be7ab 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -19,6 +19,7 @@ package android.graphics.fonts;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
+import android.graphics.Typeface;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -68,21 +69,40 @@ public final class SystemFonts {
return sAvailableFonts;
}
- Set<Font> set = new HashSet<>();
-
- for (FontFamily[] items : sFamilyMap.values()) {
- for (FontFamily family : items) {
- for (int i = 0; i < family.getSize(); ++i) {
- set.add(family.getFont(i));
+ if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+ sAvailableFonts = collectAllFonts();
+ } else {
+ Set<Font> set = new HashSet<>();
+ for (FontFamily[] items : sFamilyMap.values()) {
+ for (FontFamily family : items) {
+ for (int i = 0; i < family.getSize(); ++i) {
+ set.add(family.getFont(i));
+ }
}
}
- }
- sAvailableFonts = Collections.unmodifiableSet(set);
+ sAvailableFonts = Collections.unmodifiableSet(set);
+ }
return sAvailableFonts;
}
}
+ private static @NonNull Set<Font> collectAllFonts() {
+ final FontCustomizationParser.Result oemCustomization =
+ readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/");
+ Map<String, FontFamily[]> map = new ArrayMap<>();
+ buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", oemCustomization, map);
+ Set<Font> res = new HashSet<>();
+ for (FontFamily[] families : map.values()) {
+ for (FontFamily family : families) {
+ for (int i = 0; i < family.getSize(); ++i) {
+ res.add(family.getFont(i));
+ }
+ }
+ }
+ return res;
+ }
+
private static @Nullable ByteBuffer mmap(@NonNull String fullPath) {
try (FileInputStream file = new FileInputStream(fullPath)) {
final FileChannel fileChannel = file.getChannel();
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 39e32c694d2e..96e0559b0df6 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -122,12 +122,14 @@ android_library {
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"iconloader_base",
+ "jsr330",
"protolog-lib",
"SettingsLib",
"WindowManager-Shell-proto",
+ "jsr330"
],
kotlincflags: ["-Xjvm-default=enable"],
manifest: "AndroidManifest.xml",
min_sdk_version: "26",
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
index b86f36a14d17..341fe617b2d0 100644
--- a/libs/WindowManager/Shell/res/layout/split_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -14,7 +14,7 @@
~ limitations under the License.
-->
-<com.android.wm.shell.apppairs.DividerView
+<com.android.wm.shell.common.split.DividerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
@@ -24,4 +24,4 @@
android:id="@+id/docked_divider_background"
android:background="@color/docked_divider_background"/>
-</com.android.wm.shell.apppairs.DividerView>
+</com.android.wm.shell.common.split.DividerView>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
index ada7e1a9e3ab..45948dd9e800 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
@@ -19,6 +19,7 @@ package com.android.wm.shell;
import android.view.Gravity;
import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.letterbox.LetterboxConfigController;
import com.android.wm.shell.onehanded.OneHanded;
@@ -61,6 +62,7 @@ public final class ShellCommandHandler {
}
/** Dumps WM Shell internal state. */
+ @ExternalThread
public void dump(PrintWriter pw) {
mShellTaskOrganizer.dump(pw, "");
pw.println();
@@ -76,6 +78,7 @@ public final class ShellCommandHandler {
/** Returns {@code true} if command was found and executed. */
+ @ExternalThread
public boolean handleCommand(String[] args, PrintWriter pw) {
if (args.length < 2) {
// Argument at position 0 is "WMShell".
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
index 118f189866eb..94555de4f05c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
@@ -21,6 +21,7 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERB
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.letterbox.LetterboxTaskListener;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -56,6 +57,7 @@ public class ShellInit {
mFullscreenTaskListener = fullscreenTaskListener;
}
+ @ExternalThread
public void init() {
// Start listening for display changes
mDisplayImeController.startMonitorDisplays();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index beb9690b5cbc..174c16afc75f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -31,12 +31,14 @@ import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.window.ITaskOrganizerController;
import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
+import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -45,6 +47,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import java.io.PrintWriter;
@@ -119,7 +122,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
ShellExecutor mainExecutor, ShellExecutor animExecutor, Context context) {
super(taskOrganizerController, mainExecutor);
mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) mTransitions.register(this);
// TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
// by a controller, that class should be create while porting
// ActivityRecord#addStartingWindow to WMShell.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
index 120039de1240..10195b6a26b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
@@ -23,9 +23,9 @@ import static android.window.TransitionInfo.TRANSIT_SHOW;
import android.animation.Animator;
import android.animation.ValueAnimator;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Slog;
@@ -35,15 +35,18 @@ import android.window.ITransitionPlayer;
import android.window.TransitionInfo;
import android.window.WindowOrganizer;
+import androidx.annotation.BinderThread;
+
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
/** Plays transition animations */
-public class Transitions extends ITransitionPlayer.Stub {
+public class Transitions {
private static final String TAG = "ShellTransitions";
/** Set to {@code true} to enable shell transitions. */
@@ -54,6 +57,7 @@ public class Transitions extends ITransitionPlayer.Stub {
private final TransactionPool mTransactionPool;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ private final TransitionPlayerImpl mPlayerImpl;
/** Keeps track of currently tracked transitions and all the animations associated with each */
private final ArrayMap<IBinder, ArrayList<Animator>> mActiveTransitions = new ArrayMap<>();
@@ -64,6 +68,11 @@ public class Transitions extends ITransitionPlayer.Stub {
mTransactionPool = pool;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mPlayerImpl = new TransitionPlayerImpl();
+ }
+
+ public void register(ShellTaskOrganizer taskOrganizer) {
+ taskOrganizer.registerTransitionPlayer(mPlayerImpl);
}
// TODO(shell-transitions): real animations
@@ -115,77 +124,73 @@ public class Transitions extends ITransitionPlayer.Stub {
|| type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
}
- @Override
- public void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
+ private void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
transitionToken, info);
// start task
- mMainExecutor.execute(() -> {
- if (!mActiveTransitions.containsKey(transitionToken)) {
- Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken
- + " expecting one of " + mActiveTransitions.keySet());
- }
- if (mActiveTransitions.get(transitionToken) != null) {
- throw new IllegalStateException("Got a duplicate onTransitionReady call for "
- + transitionToken);
- }
- mActiveTransitions.put(transitionToken, new ArrayList<>());
- boolean isOpening = isOpeningType(info.getType());
- if (info.getRootLeash().isValid()) {
- t.show(info.getRootLeash());
- }
- // changes should be ordered top-to-bottom in z
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final SurfaceControl leash = change.getLeash();
- final int mode = info.getChanges().get(i).getMode();
-
- // Don't animate anything with an animating parent
- if (change.getParent() != null) {
- if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) {
- t.show(leash);
- t.setMatrix(leash, 1, 0, 0, 1);
- }
- continue;
- }
-
- t.reparent(leash, info.getRootLeash());
- t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x,
- change.getEndAbsBounds().top - info.getRootOffset().y);
- // Put all the OPEN/SHOW on top
+ if (!mActiveTransitions.containsKey(transitionToken)) {
+ Slog.e(TAG, "Got transitionReady for non-active transition " + transitionToken
+ + " expecting one of " + mActiveTransitions.keySet());
+ }
+ if (mActiveTransitions.get(transitionToken) != null) {
+ throw new IllegalStateException("Got a duplicate onTransitionReady call for "
+ + transitionToken);
+ }
+ mActiveTransitions.put(transitionToken, new ArrayList<>());
+ boolean isOpening = isOpeningType(info.getType());
+ if (info.getRootLeash().isValid()) {
+ t.show(info.getRootLeash());
+ }
+ // changes should be ordered top-to-bottom in z
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final SurfaceControl leash = change.getLeash();
+ final int mode = info.getChanges().get(i).getMode();
+
+ // Don't animate anything with an animating parent
+ if (change.getParent() != null) {
if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) {
t.show(leash);
t.setMatrix(leash, 1, 0, 0, 1);
- if (isOpening) {
- // put on top and fade in
- t.setLayer(leash, info.getChanges().size() - i);
- t.setAlpha(leash, 0.f);
- startExampleAnimation(transitionToken, leash, true /* show */);
- } else {
- // put on bottom and leave it visible without fade
- t.setLayer(leash, -i);
- t.setAlpha(leash, 1.f);
- }
- } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) {
- if (isOpening) {
- // put on bottom and leave visible without fade
- t.setLayer(leash, -i);
- } else {
- // put on top and fade out
- t.setLayer(leash, info.getChanges().size() - i);
- startExampleAnimation(transitionToken, leash, false /* show */);
- }
+ }
+ continue;
+ }
+
+ t.reparent(leash, info.getRootLeash());
+ t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x,
+ change.getEndAbsBounds().top - info.getRootOffset().y);
+ // Put all the OPEN/SHOW on top
+ if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) {
+ t.show(leash);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ if (isOpening) {
+ // put on top and fade in
+ t.setLayer(leash, info.getChanges().size() - i);
+ t.setAlpha(leash, 0.f);
+ startExampleAnimation(transitionToken, leash, true /* show */);
} else {
+ // put on bottom and leave it visible without fade
+ t.setLayer(leash, -i);
+ t.setAlpha(leash, 1.f);
+ }
+ } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) {
+ if (isOpening) {
+ // put on bottom and leave visible without fade
+ t.setLayer(leash, -i);
+ } else {
+ // put on top and fade out
t.setLayer(leash, info.getChanges().size() - i);
+ startExampleAnimation(transitionToken, leash, false /* show */);
}
+ } else {
+ t.setLayer(leash, info.getChanges().size() - i);
}
- t.apply();
- onFinish(transitionToken);
- });
+ }
+ t.apply();
+ onFinish(transitionToken);
}
- @MainThread
private void onFinish(IBinder transition) {
if (!mActiveTransitions.get(transition).isEmpty()) return;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -194,16 +199,32 @@ public class Transitions extends ITransitionPlayer.Stub {
mOrganizer.finishTransition(transition, null, null);
}
- @Override
- public void requestStartTransition(int type, @NonNull IBinder transitionToken) {
+ private void requestStartTransition(int type, @NonNull IBinder transitionToken) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: type=%d %s",
type, transitionToken);
- mMainExecutor.execute(() -> {
- if (mActiveTransitions.containsKey(transitionToken)) {
- throw new RuntimeException("Transition already started " + transitionToken);
- }
- IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */);
- mActiveTransitions.put(transition, null);
- });
+
+ if (mActiveTransitions.containsKey(transitionToken)) {
+ throw new RuntimeException("Transition already started " + transitionToken);
+ }
+ IBinder transition = mOrganizer.startTransition(type, transitionToken, null /* wct */);
+ mActiveTransitions.put(transition, null);
+ }
+
+ @BinderThread
+ private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
+ @Override
+ public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
+ SurfaceControl.Transaction transaction) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ Transitions.this.onTransitionReady(iBinder, transitionInfo, transaction);
+ });
+ }
+
+ @Override
+ public void requestStartTransition(int i, IBinder iBinder) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ Transitions.this.requestStartTransition(i, iBinder);
+ });
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
index acb9a5dae78c..834de3f15b1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java
@@ -18,11 +18,11 @@ package com.android.wm.shell;
import static android.view.Display.DEFAULT_DISPLAY;
-import android.app.WindowConfiguration;
import android.os.RemoteException;
-import android.view.WindowManagerGlobal;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
+import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener;
/**
* The singleton wrapper to communicate between WindowManagerService and WMShell features
@@ -31,32 +31,30 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder;
public class WindowManagerShellWrapper {
private static final String TAG = WindowManagerShellWrapper.class.getSimpleName();
- public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED;
-
/**
* Forwarder to which we can add multiple pinned stack listeners. Each listener will receive
* updates from the window manager service.
*/
- private PinnedStackListenerForwarder mPinnedStackListenerForwarder =
- new PinnedStackListenerForwarder();
+ private final PinnedStackListenerForwarder mPinnedStackListenerForwarder;
+
+ public WindowManagerShellWrapper(ShellExecutor shellMainExecutor) {
+ mPinnedStackListenerForwarder = new PinnedStackListenerForwarder(shellMainExecutor);
+ }
/**
* Adds a pinned stack listener, which will receive updates from the window manager service
* along with any other pinned stack listeners that were added via this method.
*/
- public void addPinnedStackListener(PinnedStackListenerForwarder.PinnedStackListener listener)
- throws
- RemoteException {
+ public void addPinnedStackListener(PinnedStackListener listener)
+ throws RemoteException {
mPinnedStackListenerForwarder.addListener(listener);
- WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
- DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
+ mPinnedStackListenerForwarder.register(DEFAULT_DISPLAY);
}
/**
* Removes a pinned stack listener.
*/
- public void removePinnedStackListener(
- PinnedStackListenerForwarder.PinnedStackListener listener) {
+ public void removePinnedStackListener(PinnedStackListener listener) {
mPinnedStackListenerForwarder.removeListener(listener);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
index 357f777e1270..176c620fa119 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
@@ -23,6 +23,8 @@ import android.view.ViewPropertyAnimator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import javax.inject.Inject;
+
/**
* Utility class to calculate general fling animation when the finger is released.
*/
@@ -368,6 +370,7 @@ public class FlingAnimationUtils {
float mX2;
float mY2;
+ @Inject
public Builder(DisplayMetrics displayMetrics) {
mDisplayMetrics = displayMetrics;
reset();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index d3032f83fc1c..cfbf8452ddae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -29,11 +29,13 @@ import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitLayout;
import java.io.PrintWriter;
@@ -42,7 +44,7 @@ import java.io.PrintWriter;
* {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
* Also includes all UI for managing the pair like the divider.
*/
-class AppPair implements ShellTaskOrganizer.TaskListener {
+class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener {
private static final String TAG = AppPair.class.getSimpleName();
private ActivityManager.RunningTaskInfo mRootTaskInfo;
@@ -55,7 +57,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
private final AppPairsController mController;
private final SyncTransactionQueue mSyncQueue;
private final DisplayController mDisplayController;
- private AppPairLayout mAppPairLayout;
+ private SplitLayout mSplitLayout;
AppPair(AppPairsController controller) {
mController = controller;
@@ -92,11 +94,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
mTaskInfo1 = task1;
mTaskInfo2 = task2;
- mAppPairLayout = new AppPairLayout(
+ mSplitLayout = new SplitLayout(
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
- mDisplayController.getDisplay(mRootTaskInfo.displayId),
- mRootTaskInfo.configuration,
- mRootTaskLeash);
+ mRootTaskInfo.configuration, this, mRootTaskLeash);
final WindowContainerToken token1 = task1.token;
final WindowContainerToken token2 = task2.token;
@@ -107,8 +107,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
.reparent(token2, mRootTaskInfo.token, true /* onTop */)
.setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW)
.setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW)
- .setBounds(token1, mAppPairLayout.getBounds1())
- .setBounds(token2, mAppPairLayout.getBounds2())
+ .setBounds(token1, mSplitLayout.getBounds1())
+ .setBounds(token2, mSplitLayout.getBounds2())
// Moving the root task to top after the child tasks were repareted , or the root
// task cannot be visible and focused.
.reorder(mRootTaskInfo.token, true);
@@ -117,6 +117,10 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
}
void unpair() {
+ unpair(null /* toTopToken */);
+ }
+
+ private void unpair(@Nullable WindowContainerToken toTopToken) {
final WindowContainerToken token1 = mTaskInfo1.token;
final WindowContainerToken token2 = mTaskInfo2.token;
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -124,16 +128,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
// Reparent out of this container and reset windowing mode.
wct.setHidden(mRootTaskInfo.token, true)
.reorder(mRootTaskInfo.token, false)
- .reparent(token1, null, false /* onTop */)
- .reparent(token2, null, false /* onTop */)
+ .reparent(token1, null, token1 == toTopToken /* onTop */)
+ .reparent(token2, null, token2 == toTopToken /* onTop */)
.setWindowingMode(token1, WINDOWING_MODE_UNDEFINED)
.setWindowingMode(token2, WINDOWING_MODE_UNDEFINED);
mController.getTaskOrganizer().applyTransaction(wct);
mTaskInfo1 = null;
mTaskInfo2 = null;
- mAppPairLayout.release();
- mAppPairLayout = null;
+ mSplitLayout.release();
+ mSplitLayout = null;
}
@Override
@@ -153,17 +157,17 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
if (mTaskLeash1 == null || mTaskLeash2 == null) return;
- mAppPairLayout.init();
- final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
- final Rect dividerBounds = mAppPairLayout.getDividerBounds();
+ mSplitLayout.init();
+ final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
+ final Rect dividerBounds = mSplitLayout.getDividerBounds();
// TODO: Is there more we need to do here?
mSyncQueue.runInSync(t -> {
- t.setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
- mTaskInfo1.positionInParent.y)
+ t.setLayer(dividerLeash, Integer.MAX_VALUE)
+ .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
+ mTaskInfo1.positionInParent.y)
.setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
mTaskInfo2.positionInParent.y)
- .setLayer(dividerLeash, Integer.MAX_VALUE)
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
.show(mRootTaskLeash)
.show(mTaskLeash1)
@@ -185,14 +189,14 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
}
mRootTaskInfo = taskInfo;
- if (mAppPairLayout != null
- && mAppPairLayout.updateConfiguration(mRootTaskInfo.configuration)) {
+ if (mSplitLayout != null
+ && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
// Update bounds when root bounds or its orientation changed.
final WindowContainerTransaction wct = new WindowContainerTransaction();
- final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
- final Rect dividerBounds = mAppPairLayout.getDividerBounds();
- final Rect bounds1 = mAppPairLayout.getBounds1();
- final Rect bounds2 = mAppPairLayout.getBounds2();
+ final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
+ final Rect dividerBounds = mSplitLayout.getDividerBounds();
+ final Rect bounds1 = mSplitLayout.getBounds1();
+ final Rect bounds2 = mSplitLayout.getBounds2();
wct.setBounds(mTaskInfo1.token, bounds1)
.setBounds(mTaskInfo2.token, bounds2);
@@ -200,7 +204,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
mSyncQueue.runInSync(t -> t
.setPosition(mTaskLeash1, bounds1.left, bounds1.top)
.setPosition(mTaskLeash2, bounds2.left, bounds2.top)
- .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top));
+ .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+ // Resets layer to divider bar to make sure it is always on top.
+ .setLayer(dividerLeash, Integer.MAX_VALUE));
}
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
@@ -242,4 +248,39 @@ class AppPair implements ShellTaskOrganizer.TaskListener {
public String toString() {
return TAG + "#" + getRootTaskId();
}
+
+ @Override
+ public void onSnappedToDismiss(boolean snappedToEnd) {
+ unpair(snappedToEnd ? mTaskInfo1.token : mTaskInfo2.token /* toTopToken */);
+ }
+
+ @Override
+ public void onBoundsChanging(SplitLayout layout) {
+ final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
+ if (dividerLeash == null) return;
+ final Rect dividerBounds = layout.getDividerBounds();
+ final Rect bounds1 = layout.getBounds1();
+ final Rect bounds2 = layout.getBounds2();
+ mSyncQueue.runInSync(t -> t
+ .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+ .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
+ .setPosition(mTaskLeash2, bounds2.left, bounds2.top));
+ }
+
+ @Override
+ public void onBoundsChanged(SplitLayout layout) {
+ final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
+ if (dividerLeash == null) return;
+ final Rect dividerBounds = layout.getDividerBounds();
+ final Rect bounds1 = layout.getBounds1();
+ final Rect bounds2 = layout.getBounds2();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mTaskInfo1.token, bounds1)
+ .setBounds(mTaskInfo2.token, bounds2);
+ mController.getTaskOrganizer().applyTransaction(wct);
+ mSyncQueue.runInSync(t -> t
+ .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+ .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
+ .setPosition(mTaskLeash2, bounds2.left, bounds2.top));
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
deleted file mode 100644
index 8c8655e1ff1f..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2020 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.wm.shell.apppairs;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.Binder;
-import android.os.IBinder;
-import android.view.Display;
-import android.view.IWindow;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
-
-import com.android.wm.shell.R;
-
-/**
- * Records and handles layout of a pair of apps.
- */
-final class AppPairLayout {
- private static final String DIVIDER_WINDOW_TITLE = "AppPairDivider";
- private final Display mDisplay;
- private final int mDividerWindowWidth;
- private final int mDividerWindowInsets;
- private final AppPairWindowManager mAppPairWindowManager;
-
- private Context mContext;
- private Rect mRootBounds;
- private DIVIDE_POLICY mDividePolicy;
-
- private SurfaceControlViewHost mViewHost;
- private SurfaceControl mDividerLeash;
-
- AppPairLayout(
- Context context,
- Display display,
- Configuration configuration,
- SurfaceControl rootLeash) {
- mContext = context.createConfigurationContext(configuration);
- mDisplay = display;
- mRootBounds = configuration.windowConfiguration.getBounds();
- mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- mDividerWindowInsets = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_insets);
-
- mAppPairWindowManager = new AppPairWindowManager(configuration, rootLeash);
- mDividePolicy = DIVIDE_POLICY.MIDDLE;
- mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
- }
-
- boolean updateConfiguration(Configuration configuration) {
- mAppPairWindowManager.setConfiguration(configuration);
- final Rect rootBounds = configuration.windowConfiguration.getBounds();
- if (isIdenticalBounds(mRootBounds, rootBounds)) {
- return false;
- }
-
- mContext = mContext.createConfigurationContext(configuration);
- mRootBounds = rootBounds;
- mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
- release();
- init();
- return true;
- }
-
- Rect getBounds1() {
- return mDividePolicy.mBounds1;
- }
-
- Rect getBounds2() {
- return mDividePolicy.mBounds2;
- }
-
- Rect getDividerBounds() {
- return mDividePolicy.mDividerBounds;
- }
-
- SurfaceControl getDividerLeash() {
- return mDividerLeash;
- }
-
- void release() {
- if (mViewHost == null) {
- return;
- }
- mViewHost.release();
- mDividerLeash = null;
- mViewHost = null;
- }
-
- void init() {
- if (mViewHost == null) {
- mViewHost = new SurfaceControlViewHost(mContext, mDisplay, mAppPairWindowManager);
- }
-
- final DividerView dividerView = (DividerView) LayoutInflater.from(mContext)
- .inflate(R.layout.split_divider, null);
-
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- mDividePolicy.mDividerBounds.width(),
- mDividePolicy.mDividerBounds.height(),
- TYPE_DOCK_DIVIDER,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
- | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
- PixelFormat.TRANSLUCENT);
- lp.token = new Binder();
- lp.setTitle(DIVIDER_WINDOW_TITLE);
- lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
-
- mViewHost.setView(dividerView, lp);
- mDividerLeash = mAppPairWindowManager.getSurfaceControl(mViewHost.getWindowToken());
- }
-
- private static boolean isIdenticalBounds(Rect bounds1, Rect bounds2) {
- return bounds1.left == bounds2.left && bounds1.top == bounds2.top
- && bounds1.right == bounds2.right && bounds1.bottom == bounds2.bottom;
- }
-
- /**
- * Indicates the policy of placing divider bar and corresponding split-screens.
- */
- // TODO(172704238): add more divide policy and provide snap to resize feature for divider bar.
- enum DIVIDE_POLICY {
- MIDDLE;
-
- void update(Rect rootBounds, int dividerWindowWidth, int dividerWindowInsets) {
- final int dividerOffset = dividerWindowWidth / 2;
- final int boundsOffset = dividerOffset - dividerWindowInsets;
-
- mDividerBounds = new Rect(rootBounds);
- mBounds1 = new Rect(rootBounds);
- mBounds2 = new Rect(rootBounds);
-
- switch (this) {
- case MIDDLE:
- default:
- if (isLandscape(rootBounds)) {
- mDividerBounds.left = rootBounds.width() / 2 - dividerOffset;
- mDividerBounds.right = rootBounds.width() / 2 + dividerOffset;
- mBounds1.left = rootBounds.width() / 2 + boundsOffset;
- mBounds2.right = rootBounds.width() / 2 - boundsOffset;
- } else {
- mDividerBounds.top = rootBounds.height() / 2 - dividerOffset;
- mDividerBounds.bottom = rootBounds.height() / 2 + dividerOffset;
- mBounds1.bottom = rootBounds.height() / 2 - boundsOffset;
- mBounds2.top = rootBounds.height() / 2 + boundsOffset;
- }
- }
- }
-
- private boolean isLandscape(Rect bounds) {
- return bounds.width() > bounds.height();
- }
-
- Rect mDividerBounds;
- Rect mBounds1;
- Rect mBounds2;
- }
-
- /**
- * WindowManger for app pair. Holds view hierarchy for the root task.
- */
- private static final class AppPairWindowManager extends WindowlessWindowManager {
- AppPairWindowManager(Configuration config, SurfaceControl rootSurface) {
- super(config, rootSurface, null /* hostInputToken */);
- }
-
- @Override
- public void setTouchRegion(IBinder window, Region region) {
- super.setTouchRegion(window, region);
- }
-
- @Override
- public SurfaceControl getSurfaceControl(IWindow window) {
- return super.getSurfaceControl(window);
- }
-
- @Override
- public void setConfiguration(Configuration configuration) {
- super.setConfiguration(configuration);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
index ef3e3e0220e7..f5aa852c87ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -20,11 +20,14 @@ import android.app.ActivityManager;
import androidx.annotation.NonNull;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
import java.io.PrintWriter;
/**
* Interface to engage app pairs feature.
*/
+@ExternalThread
public interface AppPairs {
/** Pairs indicated tasks. */
boolean pair(int task1, int task2);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java
deleted file mode 100644
index 41b5e47e7fa9..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2020 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.wm.shell.apppairs;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Stack divider for app pair.
- */
-public class DividerView extends FrameLayout {
- public DividerView(@NonNull Context context) {
- super(context);
- }
-
- public DividerView(@NonNull Context context,
- @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- void show() {
- post(() -> setVisibility(View.VISIBLE));
- }
-
- void hide() {
- post(() -> setVisibility(View.INVISIBLE));
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index aa7355b61eda..40b41e11c8aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1213,7 +1213,7 @@ public class BubbleController implements Bubbles {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
if (mStackView != null) {
- mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight));
+ mStackView.onImeVisibilityChanged(imeVisible, imeHeight);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 92d15c5feaca..fa5ac449cd54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -30,6 +30,8 @@ import android.view.View;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -40,6 +42,7 @@ import java.util.function.IntConsumer;
/**
* Interface to engage bubbles feature.
*/
+@ExternalThread
public interface Bubbles {
@Retention(SOURCE)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java
deleted file mode 100644
index 96b9f86673fc..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/AnimationThread.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2020 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.wm.shell.common;
-
-import static android.os.Process.THREAD_PRIORITY_DISPLAY;
-
-import android.annotation.NonNull;
-import android.os.HandlerThread;
-import android.util.Singleton;
-
-/**
- * A singleton thread for Shell to run animations on.
- */
-public class AnimationThread extends HandlerThread {
- private ShellExecutor mExecutor;
-
- private AnimationThread() {
- super("wmshell.anim", THREAD_PRIORITY_DISPLAY);
- }
-
- /** Get the singleton instance of this thread */
- public static AnimationThread instance() {
- return sAnimationThreadSingleton.get();
- }
-
- /**
- * @return a shared {@link ShellExecutor} associated with this thread
- * @hide
- */
- @NonNull
- public ShellExecutor getExecutor() {
- if (mExecutor == null) {
- mExecutor = new HandlerExecutor(getThreadHandler());
- }
- return mExecutor;
- }
-
- private static final Singleton<AnimationThread> sAnimationThreadSingleton =
- new Singleton<AnimationThread>() {
- @Override
- protected AnimationThread create() {
- final AnimationThread animThread = new AnimationThread();
- animThread.start();
- return animThread;
- }
- };
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 3263f79888d6..cb4584c41184 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -23,6 +23,10 @@ import android.view.IDisplayWindowRotationController;
import android.view.IWindowManager;
import android.window.WindowContainerTransaction;
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
import java.util.ArrayList;
/**
@@ -35,39 +39,18 @@ public class DisplayChangeController {
private final Handler mHandler;
private final IWindowManager mWmService;
+ private final IDisplayWindowRotationController mControllerImpl;
private final ArrayList<OnDisplayChangingListener> mRotationListener =
new ArrayList<>();
private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>();
- private final IDisplayWindowRotationController mDisplayRotationController =
- new IDisplayWindowRotationController.Stub() {
- @Override
- public void onRotateDisplay(int displayId, final int fromRotation,
- final int toRotation, IDisplayWindowRotationCallback callback) {
- mHandler.post(() -> {
- WindowContainerTransaction t = new WindowContainerTransaction();
- synchronized (mRotationListener) {
- mTmpListeners.clear();
- // Make a local copy in case the handlers add/remove themselves.
- mTmpListeners.addAll(mRotationListener);
- }
- for (OnDisplayChangingListener c : mTmpListeners) {
- c.onRotateDisplay(displayId, fromRotation, toRotation, t);
- }
- try {
- callback.continueRotateDisplay(toRotation, t);
- } catch (RemoteException e) {
- }
- });
- }
- };
-
public DisplayChangeController(Handler mainHandler, IWindowManager wmService) {
mHandler = mainHandler;
mWmService = wmService;
+ mControllerImpl = new DisplayWindowRotationControllerImpl();
try {
- mWmService.setDisplayWindowRotationController(mDisplayRotationController);
+ mWmService.setDisplayWindowRotationController(mControllerImpl);
} catch (RemoteException e) {
throw new RuntimeException("Unable to register rotation controller");
}
@@ -91,10 +74,41 @@ public class DisplayChangeController {
}
}
+ private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
+ IDisplayWindowRotationCallback callback) {
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ synchronized (mRotationListener) {
+ mTmpListeners.clear();
+ // Make a local copy in case the handlers add/remove themselves.
+ mTmpListeners.addAll(mRotationListener);
+ }
+ for (OnDisplayChangingListener c : mTmpListeners) {
+ c.onRotateDisplay(displayId, fromRotation, toRotation, t);
+ }
+ try {
+ callback.continueRotateDisplay(toRotation, t);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @BinderThread
+ private class DisplayWindowRotationControllerImpl
+ extends IDisplayWindowRotationController.Stub {
+ @Override
+ public void onRotateDisplay(int displayId, final int fromRotation,
+ final int toRotation, IDisplayWindowRotationCallback callback) {
+ mHandler.post(() -> {
+ DisplayChangeController.this.onRotateDisplay(displayId, fromRotation, toRotation,
+ callback);
+ });
+ }
+ }
+
/**
* Give a listener a chance to queue up configuration changes to execute as part of a
* display rotation. The contents of {@link #onRotateDisplay} must run synchronously.
*/
+ @ShellMainThread
public interface OnDisplayChangingListener {
/**
* Called before the display is rotated. Contents of this method must run synchronously.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 418973204add..a413c052cb6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -28,7 +28,10 @@ import android.view.Display;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
+import androidx.annotation.BinderThread;
+
import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.ArrayList;
@@ -45,6 +48,7 @@ public class DisplayController {
private final Context mContext;
private final IWindowManager mWmService;
private final DisplayChangeController mChangeController;
+ private final IDisplayWindowListener mDisplayContainerListener;
private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>();
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
@@ -57,119 +61,13 @@ public class DisplayController {
return displayManager.getDisplay(displayId);
}
- private final IDisplayWindowListener mDisplayContainerListener =
- new IDisplayWindowListener.Stub() {
- @Override
- public void onDisplayAdded(int displayId) {
- mHandler.post(() -> {
- synchronized (mDisplays) {
- if (mDisplays.get(displayId) != null) {
- return;
- }
- Display display = getDisplay(displayId);
- if (display == null) {
- // It's likely that the display is private to some app and thus not
- // accessible by system-ui.
- return;
- }
- DisplayRecord record = new DisplayRecord();
- record.mDisplayId = displayId;
- record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext
- : mContext.createDisplayContext(display);
- record.mDisplayLayout = new DisplayLayout(record.mContext, display);
- mDisplays.put(displayId, record);
- for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
- mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
- }
- }
- });
- }
-
- @Override
- public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
- mHandler.post(() -> {
- synchronized (mDisplays) {
- DisplayRecord dr = mDisplays.get(displayId);
- if (dr == null) {
- Slog.w(TAG, "Skipping Display Configuration change on non-added"
- + " display.");
- return;
- }
- Display display = getDisplay(displayId);
- if (display == null) {
- Slog.w(TAG, "Skipping Display Configuration change on invalid"
- + " display. It may have been removed.");
- return;
- }
- Context perDisplayContext = mContext;
- if (displayId != Display.DEFAULT_DISPLAY) {
- perDisplayContext = mContext.createDisplayContext(display);
- }
- dr.mContext = perDisplayContext.createConfigurationContext(newConfig);
- dr.mDisplayLayout = new DisplayLayout(dr.mContext, display);
- for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
- mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
- displayId, newConfig);
- }
- }
- });
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- mHandler.post(() -> {
- synchronized (mDisplays) {
- if (mDisplays.get(displayId) == null) {
- return;
- }
- for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
- mDisplayChangedListeners.get(i).onDisplayRemoved(displayId);
- }
- mDisplays.remove(displayId);
- }
- });
- }
-
- @Override
- public void onFixedRotationStarted(int displayId, int newRotation) {
- mHandler.post(() -> {
- synchronized (mDisplays) {
- if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
- Slog.w(TAG, "Skipping onFixedRotationStarted on unknown"
- + " display, displayId=" + displayId);
- return;
- }
- for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
- mDisplayChangedListeners.get(i).onFixedRotationStarted(
- displayId, newRotation);
- }
- }
- });
- }
-
- @Override
- public void onFixedRotationFinished(int displayId) {
- mHandler.post(() -> {
- synchronized (mDisplays) {
- if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
- Slog.w(TAG, "Skipping onFixedRotationFinished on unknown"
- + " display, displayId=" + displayId);
- return;
- }
- for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
- mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId);
- }
- }
- });
- }
- };
-
public DisplayController(Context context, Handler handler,
IWindowManager wmService) {
mHandler = handler;
mContext = context;
mWmService = wmService;
mChangeController = new DisplayChangeController(mHandler, mWmService);
+ mDisplayContainerListener = new DisplayWindowListenerImpl();
try {
mWmService.registerDisplayWindowListener(mDisplayContainerListener);
} catch (RemoteException e) {
@@ -232,18 +130,146 @@ public class DisplayController {
mChangeController.removeRotationListener(controller);
}
+ private void onDisplayAdded(int displayId) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) != null) {
+ return;
+ }
+ Display display = getDisplay(displayId);
+ if (display == null) {
+ // It's likely that the display is private to some app and thus not
+ // accessible by system-ui.
+ return;
+ }
+ DisplayRecord record = new DisplayRecord();
+ record.mDisplayId = displayId;
+ record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext
+ : mContext.createDisplayContext(display);
+ record.mDisplayLayout = new DisplayLayout(record.mContext, display);
+ mDisplays.put(displayId, record);
+ for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
+ mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
+ }
+ }
+ }
+
+ private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ synchronized (mDisplays) {
+ DisplayRecord dr = mDisplays.get(displayId);
+ if (dr == null) {
+ Slog.w(TAG, "Skipping Display Configuration change on non-added"
+ + " display.");
+ return;
+ }
+ Display display = getDisplay(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Skipping Display Configuration change on invalid"
+ + " display. It may have been removed.");
+ return;
+ }
+ Context perDisplayContext = mContext;
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ perDisplayContext = mContext.createDisplayContext(display);
+ }
+ dr.mContext = perDisplayContext.createConfigurationContext(newConfig);
+ dr.mDisplayLayout = new DisplayLayout(dr.mContext, display);
+ for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
+ mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
+ displayId, newConfig);
+ }
+ }
+ }
+
+ private void onDisplayRemoved(int displayId) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) == null) {
+ return;
+ }
+ for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+ mDisplayChangedListeners.get(i).onDisplayRemoved(displayId);
+ }
+ mDisplays.remove(displayId);
+ }
+ }
+
+ private void onFixedRotationStarted(int displayId, int newRotation) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+ Slog.w(TAG, "Skipping onFixedRotationStarted on unknown"
+ + " display, displayId=" + displayId);
+ return;
+ }
+ for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+ mDisplayChangedListeners.get(i).onFixedRotationStarted(
+ displayId, newRotation);
+ }
+ }
+ }
+
+ private void onFixedRotationFinished(int displayId) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+ Slog.w(TAG, "Skipping onFixedRotationFinished on unknown"
+ + " display, displayId=" + displayId);
+ return;
+ }
+ for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+ mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId);
+ }
+ }
+ }
+
private static class DisplayRecord {
int mDisplayId;
Context mContext;
DisplayLayout mDisplayLayout;
}
+ @BinderThread
+ private class DisplayWindowListenerImpl extends IDisplayWindowListener.Stub {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ mHandler.post(() -> {
+ DisplayController.this.onDisplayAdded(displayId);
+ });
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ mHandler.post(() -> {
+ DisplayController.this.onDisplayConfigurationChanged(displayId, newConfig);
+ });
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mHandler.post(() -> {
+ DisplayController.this.onDisplayRemoved(displayId);
+ });
+ }
+
+ @Override
+ public void onFixedRotationStarted(int displayId, int newRotation) {
+ mHandler.post(() -> {
+ DisplayController.this.onFixedRotationStarted(displayId, newRotation);
+ });
+ }
+
+ @Override
+ public void onFixedRotationFinished(int displayId) {
+ mHandler.post(() -> {
+ DisplayController.this.onFixedRotationFinished(displayId);
+ });
+ }
+ }
+
/**
* Gets notified when a display is added/removed to the WM hierarchy and when a display's
* window-configuration changes.
*
* @see IDisplayWindowListener
*/
+ @ShellMainThread
public interface OnDisplaysChangedListener {
/**
* Called when a display has been added to the WM hierarchy.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index ea18a19c2ee5..3fbd7ed0ec5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -24,7 +24,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
@@ -40,6 +39,8 @@ import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import androidx.annotation.BinderThread;
+
import com.android.internal.view.IInputMethodManager;
import java.util.ArrayList;
@@ -197,6 +198,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mRotation = initialRotation;
}
+ @BinderThread
@Override
public void insetsChanged(InsetsState insetsState) {
mExecutor.execute(() -> {
@@ -204,6 +206,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
return;
}
+ mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME);
+
final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
final Rect newFrame = newSource.getFrame();
final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame();
@@ -216,6 +220,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
});
}
+ @BinderThread
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
@@ -266,6 +271,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
+ @BinderThread
@Override
public void showInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
@@ -275,6 +281,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
}
+ @BinderThread
@Override
public void hideInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
@@ -284,6 +291,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
mExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
}
+ @BinderThread
@Override
public void topFocusedWindowChanged(String packageName) {
// no-op
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index cd75840b8c71..fa0a75c2d364 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -28,6 +28,17 @@ public class HandlerExecutor implements ShellExecutor {
}
@Override
+ public void execute(@NonNull Runnable command) {
+ if (mHandler.getLooper().isCurrentThread()) {
+ command.run();
+ return;
+ }
+ if (!mHandler.post(command)) {
+ throw new RuntimeException(mHandler + " is probably exiting");
+ }
+ }
+
+ @Override
public void executeDelayed(@NonNull Runnable r, long delayMillis) {
if (!mHandler.postDelayed(r, delayMillis)) {
throw new RuntimeException(mHandler + " is probably exiting");
@@ -38,11 +49,4 @@ public class HandlerExecutor implements ShellExecutor {
public void removeCallbacks(@NonNull Runnable r) {
mHandler.removeCallbacks(r);
}
-
- @Override
- public void execute(@NonNull Runnable command) {
- if (!mHandler.post(command)) {
- throw new RuntimeException(mHandler + " is probably exiting");
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index aafe2407a1ea..22b831b7565e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -16,13 +16,40 @@
package com.android.wm.shell.common;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
/**
* Super basic Executor interface that adds support for delayed execution and removing callbacks.
* Intended to wrap Handler while better-supporting testing.
*/
public interface ShellExecutor extends Executor {
+
+ /**
+ * Executes the given runnable. If the caller is running on the same looper as this executor,
+ * the runnable must be executed immediately.
+ */
+ @Override
+ void execute(Runnable runnable);
+
+ /**
+ * Executes the given runnable in a blocking call. If the caller is running on the same looper
+ * as this executor, the runnable must be executed immediately.
+ *
+ * @throws InterruptedException if runnable does not return in the time specified by
+ * {@param waitTimeout}
+ */
+ default void executeBlocking(Runnable runnable, int waitTimeout, TimeUnit waitTimeUnit)
+ throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ execute(() -> {
+ runnable.run();
+ latch.countDown();
+ });
+ latch.await(waitTimeout, waitTimeUnit);
+ }
+
/**
* See {@link android.os.Handler#postDelayed(Runnable, long)}.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index 9cb125087cd9..7321dc88770d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -24,6 +24,10 @@ import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
import java.util.ArrayList;
/**
@@ -151,6 +155,7 @@ public final class SyncTransactionQueue {
mHandler.postDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
}
+ @BinderThread
@Override
public void onTransactionReady(int id,
@NonNull SurfaceControl.Transaction t) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
index 0f6dd93f9c16..5e077188c415 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java
@@ -19,12 +19,10 @@ package com.android.wm.shell.common;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ITaskStackListener;
-import android.app.TaskInfo;
import android.content.ComponentName;
import android.os.IBinder;
import androidx.annotation.BinderThread;
-import androidx.annotation.MainThread;
/**
* An interface to track task stack changes. Classes should implement this instead of
@@ -32,85 +30,61 @@ import androidx.annotation.MainThread;
*/
public interface TaskStackListenerCallback {
- @MainThread
default void onRecentTaskListUpdated() { }
- @MainThread
default void onRecentTaskListFrozenChanged(boolean frozen) { }
@BinderThread
default void onTaskStackChangedBackground() { }
- @MainThread
default void onTaskStackChanged() { }
- @MainThread
default void onTaskProfileLocked(int taskId, int userId) { }
- @MainThread
default void onTaskDisplayChanged(int taskId, int newDisplayId) { }
- @MainThread
default void onTaskCreated(int taskId, ComponentName componentName) { }
- @MainThread
default void onTaskRemoved(int taskId) { }
- @MainThread
default void onTaskMovedToFront(int taskId) { }
- @MainThread
default void onTaskMovedToFront(RunningTaskInfo taskInfo) {
onTaskMovedToFront(taskInfo.taskId);
}
- @MainThread
default void onTaskDescriptionChanged(RunningTaskInfo taskInfo) { }
- @MainThread
default void onTaskSnapshotChanged(int taskId, ActivityManager.TaskSnapshot snapshot) { }
- @MainThread
default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { }
- @MainThread
default void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
boolean clearedTask, boolean wasVisible) { }
- @MainThread
default void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
- @MainThread
default void onActivityUnpinned() { }
- @MainThread
default void onActivityForcedResizable(String packageName, int taskId, int reason) { }
- @MainThread
default void onActivityDismissingDockedStack() { }
- @MainThread
default void onActivityLaunchOnSecondaryDisplayFailed() { }
- @MainThread
default void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) {
onActivityLaunchOnSecondaryDisplayFailed();
}
- @MainThread
default void onActivityLaunchOnSecondaryDisplayRerouted() { }
- @MainThread
default void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) {
onActivityLaunchOnSecondaryDisplayRerouted();
}
- @MainThread
default void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
- @MainThread
default void onActivityRotation(int displayId) { }
- @MainThread
default void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java
new file mode 100644
index 000000000000..4009ad21b9b8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ChoreographerSfVsync.java
@@ -0,0 +1,18 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Annotates a method that or qualifies a provider runs aligned to the Choreographer SF vsync
+ * instead of the app vsync.
+ */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ChoreographerSfVsync {} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java
new file mode 100644
index 000000000000..7560f71d1f98
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ExternalThread.java
@@ -0,0 +1,15 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/** Annotates a method or class that is called from an external thread to the Shell threads. */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExternalThread {} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java
new file mode 100644
index 000000000000..0479f8780c79
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellAnimationThread.java
@@ -0,0 +1,15 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/** Annotates a method or qualifies a provider that runs on the Shell animation-thread */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ShellAnimationThread {} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java
new file mode 100644
index 000000000000..423f4ce3bfd4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/annotations/ShellMainThread.java
@@ -0,0 +1,15 @@
+package com.android.wm.shell.common.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/** Annotates a method or qualifies a provider that runs on the Shell main-thread */
+@Documented
+@Inherited
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ShellMainThread {} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
new file mode 100644
index 000000000000..50d9fe8629ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.common.split;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.SurfaceControlViewHost;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+
+/**
+ * Stack divider for app pair.
+ */
+// TODO(b/172704238): add handle view to indicate touching status.
+public class DividerView extends FrameLayout implements View.OnTouchListener {
+ private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+
+ private SplitLayout mSplitLayout;
+ private SurfaceControlViewHost mViewHost;
+ private DragListener mDragListener;
+
+ private VelocityTracker mVelocityTracker;
+ private boolean mMoving;
+ private int mStartPos;
+
+ public DividerView(@NonNull Context context) {
+ super(context);
+ }
+
+ public DividerView(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /** Sets up essential dependencies of the divider bar. */
+ public void setup(
+ SplitLayout layout,
+ SurfaceControlViewHost viewHost,
+ @Nullable DragListener dragListener) {
+ mSplitLayout = layout;
+ mViewHost = viewHost;
+ mDragListener = dragListener;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setOnTouchListener(this);
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mSplitLayout == null) {
+ return false;
+ }
+
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ final boolean isLandscape = isLandscape();
+ // Using raw xy to prevent lost track of motion events while moving divider bar.
+ final int touchPos = isLandscape ? (int) event.getRawX() : (int) event.getRawY();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+ setSlippery(false);
+ mStartPos = touchPos;
+ mMoving = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mVelocityTracker.addMovement(event);
+ if (!mMoving && Math.abs(touchPos - mStartPos) > mTouchSlop) {
+ mStartPos = touchPos;
+ mMoving = true;
+ if (mDragListener != null) {
+ mDragListener.onDragStart();
+ }
+ }
+ if (mMoving) {
+ final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
+ mSplitLayout.updateDividePosition(position);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mVelocityTracker.addMovement(event);
+ mVelocityTracker.computeCurrentVelocity(1000 /* units */);
+ final float velocity = isLandscape
+ ? mVelocityTracker.getXVelocity()
+ : mVelocityTracker.getYVelocity();
+ setSlippery(true);
+ mMoving = false;
+ if (mDragListener != null) {
+ mDragListener.onDragEnd();
+ }
+
+ final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
+ final DividerSnapAlgorithm.SnapTarget snapTarget =
+ mSplitLayout.findSnapTarget(position, velocity);
+ mSplitLayout.setSnapTarget(snapTarget);
+ break;
+ }
+ return true;
+ }
+
+ private void setSlippery(boolean slippery) {
+ if (mViewHost == null) {
+ return;
+ }
+
+ final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
+ final boolean isSlippery = (lp.flags & FLAG_SLIPPERY) != 0;
+ if (isSlippery == slippery) {
+ return;
+ }
+
+ if (slippery) {
+ lp.flags |= FLAG_SLIPPERY;
+ } else {
+ lp.flags &= ~FLAG_SLIPPERY;
+ }
+ mViewHost.relayout(lp);
+ }
+
+ private boolean isLandscape() {
+ return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+ }
+
+ /** Monitors dragging action of the divider bar. */
+ // TODO(b/172704238): add listeners to deal with resizing state of the app windows.
+ public interface DragListener {
+ /** Called when start dragging. */
+ void onDragStart();
+ /** Called when stop dragging. */
+ void onDragEnd();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
new file mode 100644
index 000000000000..e11037f55cfa
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.common.split;
+
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
+import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+
+/**
+ * Records and handles layout of splits. Helps to calculate proper bounds when configuration or
+ * divide position changes.
+ */
+public class SplitLayout {
+ private final int mDividerWindowWidth;
+ private final int mDividerInsets;
+ private final int mDividerSize;
+
+ private final Rect mRootBounds = new Rect();
+ private final Rect mDividerBounds = new Rect();
+ private final Rect mBounds1 = new Rect();
+ private final Rect mBounds2 = new Rect();
+ private final LayoutChangeListener mLayoutChangeListener;
+ private final SplitWindowManager mSplitWindowManager;
+
+ private Context mContext;
+ private DividerSnapAlgorithm mDividerSnapAlgorithm;
+ private int mDividePosition;
+
+ public SplitLayout(Context context, Configuration configuration,
+ LayoutChangeListener layoutChangeListener, SurfaceControl rootLeash) {
+ mContext = context.createConfigurationContext(configuration);
+ mLayoutChangeListener = layoutChangeListener;
+ mSplitWindowManager = new SplitWindowManager(mContext, configuration, rootLeash);
+
+ mDividerWindowWidth = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ mDividerInsets = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+ mDividerSize = mDividerWindowWidth - mDividerInsets * 2;
+
+ mRootBounds.set(configuration.windowConfiguration.getBounds());
+ mDividerSnapAlgorithm = getSnapAlgorithm(context.getResources(), mRootBounds);
+ mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
+ updateBounds(mDividePosition);
+ }
+
+ /** Gets bounds of the primary split. */
+ public Rect getBounds1() {
+ return mBounds1;
+ }
+
+ /** Gets bounds of the secondary split. */
+ public Rect getBounds2() {
+ return mBounds2;
+ }
+
+ /** Gets bounds of divider window. */
+ public Rect getDividerBounds() {
+ return mDividerBounds;
+ }
+
+ /** Returns leash of the current divider bar. */
+ @Nullable
+ public SurfaceControl getDividerLeash() {
+ return mSplitWindowManager == null ? null : mSplitWindowManager.getSurfaceControl();
+ }
+
+ int getDividePosition() {
+ return mDividePosition;
+ }
+
+ /** Applies new configuration, returns {@code false} if there's no effect to the layout. */
+ public boolean updateConfiguration(Configuration configuration) {
+ final Rect rootBounds = configuration.windowConfiguration.getBounds();
+ if (mRootBounds.equals(rootBounds)) {
+ return false;
+ }
+
+ mContext = mContext.createConfigurationContext(configuration);
+ mSplitWindowManager.setConfiguration(configuration);
+ mRootBounds.set(rootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext.getResources(), mRootBounds);
+ mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
+ updateBounds(mDividePosition);
+ release();
+ init();
+ return true;
+ }
+
+ /** Updates recording bounds of divider window and both of the splits. */
+ private void updateBounds(int position) {
+ mDividerBounds.set(mRootBounds);
+ mBounds1.set(mRootBounds);
+ mBounds2.set(mRootBounds);
+ if (isLandscape(mRootBounds)) {
+ mDividerBounds.left = position - mDividerInsets;
+ mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth;
+ mBounds1.right = mBounds1.left + position;
+ mBounds2.left = mBounds1.right + mDividerSize;
+ } else {
+ mDividerBounds.top = position - mDividerInsets;
+ mDividerBounds.bottom = mDividerBounds.top + mDividerWindowWidth;
+ mBounds1.bottom = mBounds1.top + position;
+ mBounds2.top = mBounds1.bottom + mDividerSize;
+ }
+ }
+
+ /** Inflates {@link DividerView} on the root surface. */
+ public void init() {
+ mSplitWindowManager.init(this);
+ }
+
+ /** Releases the surface holding the current {@link DividerView}. */
+ public void release() {
+ mSplitWindowManager.release();
+ }
+
+ /**
+ * Updates bounds with the passing position. Usually used to update recording bounds while
+ * performing animation or dragging divider bar to resize the splits.
+ */
+ public void updateDividePosition(int position) {
+ updateBounds(position);
+ mLayoutChangeListener.onBoundsChanging(this);
+ }
+
+ /**
+ * Sets new divide position and updates bounds correspondingly. Notifies listener if the new
+ * target indicates dismissing split.
+ */
+ public void setSnapTarget(DividerSnapAlgorithm.SnapTarget snapTarget) {
+ switch(snapTarget.flag) {
+ case FLAG_DISMISS_START:
+ mLayoutChangeListener.onSnappedToDismiss(false /* snappedToEnd */);
+ break;
+ case FLAG_DISMISS_END:
+ mLayoutChangeListener.onSnappedToDismiss(true /* snappedToEnd */);
+ break;
+ default:
+ mDividePosition = snapTarget.position;
+ updateBounds(mDividePosition);
+ mLayoutChangeListener.onBoundsChanged(this);
+ break;
+ }
+ }
+
+ /**
+ * Returns {@link DividerSnapAlgorithm.SnapTarget} which matches passing position and velocity.
+ */
+ public DividerSnapAlgorithm.SnapTarget findSnapTarget(int position, float velocity) {
+ return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity);
+ }
+
+ private DividerSnapAlgorithm getSnapAlgorithm(Resources resources, Rect rootBounds) {
+ final boolean isLandscape = isLandscape(rootBounds);
+ return new DividerSnapAlgorithm(
+ resources,
+ rootBounds.width(),
+ rootBounds.height(),
+ mDividerSize,
+ !isLandscape,
+ new Rect() /* insets */,
+ isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
+ }
+
+ private static boolean isLandscape(Rect bounds) {
+ return bounds.width() > bounds.height();
+ }
+
+ /** Listens layout change event. */
+ public interface LayoutChangeListener {
+ /** Calls when dismissing split. */
+ void onSnappedToDismiss(boolean snappedToEnd);
+ /** Calls when the bounds is changing due to animation or dragging divider bar. */
+ void onBoundsChanging(SplitLayout layout);
+ /** Calls when the target bounds changed. */
+ void onBoundsChanged(SplitLayout layout);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
new file mode 100644
index 000000000000..542867d83e29
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.common.split;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/**
+ * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split.
+ */
+public final class SplitWindowManager extends WindowlessWindowManager {
+ private static final String DIVIDER_WINDOW_TITLE = "SplitDivider";
+
+ private Context mContext;
+ private SurfaceControlViewHost mViewHost;
+
+ public SplitWindowManager(Context context, Configuration config, SurfaceControl rootSurface) {
+ super(config, rootSurface, null /* hostInputToken */);
+ mContext = context.createConfigurationContext(config);
+ }
+
+ @Override
+ public void setTouchRegion(IBinder window, Region region) {
+ super.setTouchRegion(window, region);
+ }
+
+ @Override
+ public SurfaceControl getSurfaceControl(IWindow window) {
+ return super.getSurfaceControl(window);
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ /** Inflates {@link DividerView} on to the root surface. */
+ void init(SplitLayout splitLayout) {
+ if (mViewHost == null) {
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ }
+
+ final Rect dividerBounds = splitLayout.getDividerBounds();
+ final DividerView dividerView = (DividerView) LayoutInflater.from(mContext)
+ .inflate(R.layout.split_divider, null /* root */);
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
+ | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
+ PixelFormat.TRANSLUCENT);
+ lp.token = new Binder();
+ lp.setTitle(DIVIDER_WINDOW_TITLE);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ mViewHost.setView(dividerView, lp);
+ dividerView.setup(splitLayout, mViewHost, null /* dragListener */);
+ }
+
+ /**
+ * Releases the surface control of the current {@link DividerView} and tear down the view
+ * hierarchy.
+ */
+ void release() {
+ if (mViewHost == null) return;
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ /**
+ * Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
+ * feasible.
+ */
+ @Nullable
+ SurfaceControl getSurfaceControl() {
+ return mViewHost == null ? null : getSurfaceControl(mViewHost.getWindowToken());
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
index 38e0519b7a90..3a2f0da6bf03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
@@ -20,11 +20,14 @@ import android.content.res.Configuration;
import androidx.annotation.NonNull;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
import java.io.PrintWriter;
/**
* Interface to engage hide display cutout feature.
*/
+@ExternalThread
public interface HideDisplayCutout {
/**
* Notifies {@link Configuration} changed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 9bb709f9a82a..821a00703adf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.onehanded;
import androidx.annotation.NonNull;
+import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
import java.io.PrintWriter;
@@ -25,6 +26,7 @@ import java.io.PrintWriter;
/**
* Interface to engage one handed feature.
*/
+@ExternalThread
public interface OneHanded {
/**
* Return one handed settings enabled or not.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
index 993e0e7ed016..5593268588fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java
@@ -19,12 +19,16 @@ package com.android.wm.shell.pip;
import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
+import android.os.RemoteException;
import android.view.DisplayInfo;
-import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
+import android.view.WindowManagerGlobal;
+
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.ShellExecutor;
import java.util.ArrayList;
-import java.util.List;
/**
* PinnedStackListener that simply forwards all calls to each listener added via
@@ -32,8 +36,15 @@ import java.util.List;
* {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any
* previously set listener.
*/
-public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
- private List<PinnedStackListener> mListeners = new ArrayList<>();
+public class PinnedStackListenerForwarder {
+
+ private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl();
+ private final ShellExecutor mShellMainExecutor;
+ private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>();
+
+ public PinnedStackListenerForwarder(ShellExecutor shellMainExecutor) {
+ mShellMainExecutor = shellMainExecutor;
+ }
/** Adds a listener to receive updates from the WindowManagerService. */
public void addListener(PinnedStackListener listener) {
@@ -45,69 +56,110 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
mListeners.remove(listener);
}
- @Override
- public void onListenerRegistered(IPinnedStackController controller) {
- for (PinnedStackListener listener : mListeners) {
- listener.onListenerRegistered(controller);
- }
+ public void register(int displayId) throws RemoteException {
+ WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
+ displayId, mListenerImpl);
}
- @Override
- public void onMovementBoundsChanged(boolean fromImeAdjustment) {
+ private void onMovementBoundsChanged(boolean fromImeAdjustment) {
for (PinnedStackListener listener : mListeners) {
listener.onMovementBoundsChanged(fromImeAdjustment);
}
}
- @Override
- public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ private void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
for (PinnedStackListener listener : mListeners) {
listener.onImeVisibilityChanged(imeVisible, imeHeight);
}
}
- @Override
- public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
+ private void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
for (PinnedStackListener listener : mListeners) {
listener.onActionsChanged(actions);
}
}
- @Override
- public void onActivityHidden(ComponentName componentName) {
+ private void onActivityHidden(ComponentName componentName) {
for (PinnedStackListener listener : mListeners) {
listener.onActivityHidden(componentName);
}
}
- @Override
- public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ private void onDisplayInfoChanged(DisplayInfo displayInfo) {
for (PinnedStackListener listener : mListeners) {
listener.onDisplayInfoChanged(displayInfo);
}
}
- @Override
- public void onConfigurationChanged() {
+ private void onConfigurationChanged() {
for (PinnedStackListener listener : mListeners) {
listener.onConfigurationChanged();
}
}
- @Override
- public void onAspectRatioChanged(float aspectRatio) {
+ private void onAspectRatioChanged(float aspectRatio) {
for (PinnedStackListener listener : mListeners) {
listener.onAspectRatioChanged(aspectRatio);
}
}
+ @BinderThread
+ private class PinnedStackListenerImpl extends IPinnedStackListener.Stub {
+ @Override
+ public void onMovementBoundsChanged(boolean fromImeAdjustment) {
+ mShellMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onMovementBoundsChanged(fromImeAdjustment);
+ });
+ }
+
+ @Override
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ mShellMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onImeVisibilityChanged(imeVisible, imeHeight);
+ });
+ }
+
+ @Override
+ public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
+ mShellMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onActionsChanged(actions);
+ });
+ }
+
+ @Override
+ public void onActivityHidden(ComponentName componentName) {
+ mShellMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onActivityHidden(componentName);
+ });
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ mShellMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onDisplayInfoChanged(displayInfo);
+ });
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ mShellMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onConfigurationChanged();
+ });
+ }
+
+ @Override
+ public void onAspectRatioChanged(float aspectRatio) {
+ mShellMainExecutor.execute(() -> {
+ PinnedStackListenerForwarder.this.onAspectRatioChanged(aspectRatio);
+ });
+ }
+ }
+
/**
* A counterpart of {@link IPinnedStackListener} with empty implementations.
* Subclasses can ignore those methods they do not intend to take action upon.
*/
public static class PinnedStackListener {
- public void onListenerRegistered(IPinnedStackController controller) {}
-
public void onMovementBoundsChanged(boolean fromImeAdjustment) {}
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 9fa222ad4fdd..da9ce0aacedc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -17,12 +17,12 @@
package com.android.wm.shell.pip;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
+import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import java.io.PrintWriter;
@@ -31,6 +31,7 @@ import java.util.function.Consumer;
/**
* Interface to engage picture in picture feature.
*/
+@ExternalThread
public interface Pip {
/**
* Closes PIP (PIPed activity and PIP system UI).
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index d82946269ee8..fe018115408a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -54,6 +54,7 @@ public class PipAnimationController {
public static final int TRANSITION_DIRECTION_LEAVE_PIP = 3;
public static final int TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN = 4;
public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5;
+ public static final int TRANSITION_DIRECTION_SNAP_AFTER_RESIZE = 6;
@IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
TRANSITION_DIRECTION_NONE,
@@ -61,7 +62,8 @@ public class PipAnimationController {
TRANSITION_DIRECTION_TO_PIP,
TRANSITION_DIRECTION_LEAVE_PIP,
TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN,
- TRANSITION_DIRECTION_REMOVE_STACK
+ TRANSITION_DIRECTION_REMOVE_STACK,
+ TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
})
@Retention(RetentionPolicy.SOURCE)
public @interface TransitionDirection {}
@@ -109,13 +111,27 @@ public class PipAnimationController {
}
@SuppressWarnings("unchecked")
+ /**
+ * Construct and return an animator that animates from the {@param startBounds} to the
+ * {@param endBounds} with the given {@param direction}. If {@param direction} is type
+ * {@link ANIM_TYPE_BOUNDS}, then {@param sourceHintRect} will be used to animate
+ * in a better, more smooth manner.
+ *
+ * In the case where one wants to start animation during an intermediate animation (for example,
+ * if the user is currently doing a pinch-resize, and upon letting go now PiP needs to animate
+ * to the correct snap fraction region), then provide the base bounds, which is current PiP
+ * leash bounds before transformation/any animation. This is so when we try to construct
+ * the different transformation matrices for the animation, we are constructing this based off
+ * the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
+ */
@VisibleForTesting
- public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
- Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction) {
+ public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds,
+ Rect startBounds, Rect endBounds, Rect sourceHintRect,
+ @PipAnimationController.TransitionDirection int direction) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
- direction));
+ PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds,
+ sourceHintRect, direction));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -130,8 +146,8 @@ public class PipAnimationController {
} else {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
- direction));
+ PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds,
+ sourceHintRect, direction));
}
return mCurrentAnimator;
}
@@ -180,6 +196,7 @@ public class PipAnimationController {
private final @AnimationType int mAnimationType;
private final Rect mDestinationBounds = new Rect();
+ private T mBaseValue;
protected T mCurrentValue;
protected T mStartValue;
private T mEndValue;
@@ -190,10 +207,11 @@ public class PipAnimationController {
private @TransitionDirection int mTransitionDirection;
private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
- Rect destinationBounds, T startValue, T endValue) {
+ Rect destinationBounds, T baseValue, T startValue, T endValue) {
mLeash = leash;
mAnimationType = animationType;
mDestinationBounds.set(destinationBounds);
+ mBaseValue = baseValue;
mStartValue = startValue;
mEndValue = endValue;
addListener(this);
@@ -263,6 +281,10 @@ public class PipAnimationController {
return mStartValue;
}
+ T getBaseValue() {
+ return mBaseValue;
+ }
+
@VisibleForTesting
public T getEndValue() {
return mEndValue;
@@ -334,7 +356,7 @@ public class PipAnimationController {
static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
Rect destinationBounds, float startValue, float endValue) {
return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
- destinationBounds, startValue, endValue) {
+ destinationBounds, startValue, startValue, endValue) {
@Override
void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction) {
@@ -367,7 +389,7 @@ public class PipAnimationController {
}
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
- Rect startValue, Rect endValue, Rect sourceHintRect,
+ Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
@@ -375,7 +397,7 @@ public class PipAnimationController {
if (isOutPipDirection(direction)) {
initialSourceValue = new Rect(endValue);
} else {
- initialSourceValue = new Rect(startValue);
+ initialSourceValue = new Rect(baseValue);
}
final Rect sourceHintRectInsets;
@@ -391,22 +413,24 @@ public class PipAnimationController {
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
- endValue, new Rect(startValue), new Rect(endValue)) {
+ endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
@Override
void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction) {
+ final Rect base = getBaseValue();
final Rect start = getStartValue();
final Rect end = getEndValue();
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
setCurrentValue(bounds);
if (inScaleTransition() || sourceHintRect == null) {
+
if (isOutPipDirection(direction)) {
getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
} else {
- getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
+ getSurfaceTransactionHelper().scale(tx, leash, base, bounds);
}
} else {
final Rect insets;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
new file mode 100644
index 000000000000..8d9ad4d1b96c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.pip;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.RemoteAction;
+import android.content.pm.ParceledListSlice;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+/**
+ * Interface to allow {@link com.android.wm.shell.pip.PipTaskOrganizer} to call into
+ * PiP menu when certain events happen (task appear/vanish, PiP move, etc.)
+ */
+public interface PipMenuController {
+
+ String MENU_WINDOW_TITLE = "PipMenuView";
+
+ /**
+ * Called when
+ * {@link PipTaskOrganizer#onTaskAppeared(RunningTaskInfo, SurfaceControl)}
+ * is called.
+ */
+ void attach(SurfaceControl leash);
+
+ /**
+ * Called when
+ * {@link PipTaskOrganizer#onTaskVanished(RunningTaskInfo)} is called.
+ */
+ void detach();
+
+ /**
+ * Check if menu is visible or not.
+ */
+ boolean isMenuVisible();
+
+ /**
+ * Show the PIP menu.
+ */
+ void showMenu();
+
+ /**
+ * Given a set of actions, update the menu.
+ */
+ void setAppActions(ParceledListSlice<RemoteAction> appActions);
+
+ /**
+ * Resize the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a
+ * need to synchronize the movements on the same frame as PiP.
+ */
+ default void resizePipMenu(@Nullable SurfaceControl pipLeash,
+ @Nullable SurfaceControl.Transaction t,
+ Rect destinationBounds) {}
+
+ /**
+ * Move the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a
+ * need to synchronize the movements on the same frame as PiP.
+ */
+ default void movePipMenu(@Nullable SurfaceControl pipLeash,
+ @Nullable SurfaceControl.Transaction t,
+ Rect destinationBounds) {}
+
+ /**
+ * Update the PiP menu with the given bounds for re-layout purposes.
+ */
+ default void updateMenuBounds(Rect destinationBounds) {}
+
+ /**
+ * Returns a default LayoutParams for the PIP Menu.
+ * @param width the PIP stack width.
+ * @param height the PIP stack height.
+ */
+ default WindowManager.LayoutParams getPipMenuLayoutParams(String title, int width, int height) {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY;
+ lp.setTitle(title);
+ return lp;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 7cc2a419354e..167b9f9975f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -30,6 +30,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
@@ -63,7 +64,6 @@ import com.android.internal.os.SomeArgs;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.pip.phone.PipMenuActivityController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipUpdateThread;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -135,8 +135,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final Handler mUpdateHandler;
private final PipBoundsState mPipBoundsState;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
- // TODO(b/172286265): Remove dependency on .pip.PHONE.PipMenuActivityController
- private final PipMenuActivityController mMenuActivityController;
+ private final @NonNull PipMenuController mPipMenuController;
private final PipAnimationController mPipAnimationController;
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
@@ -264,7 +263,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
@NonNull PipBoundsAlgorithm boundsHandler,
- PipMenuActivityController menuActivityController,
+ @NonNull PipMenuController pipMenuController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional,
@NonNull DisplayController displayController,
@@ -274,7 +273,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
- mMenuActivityController = menuActivityController;
+ mPipMenuController = pipMenuController;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = surfaceTransactionHelper;
@@ -501,9 +500,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mOnDisplayIdChangeCallback.accept(info.displayId);
}
- if (mMenuActivityController != null) {
- mMenuActivityController.onTaskAppeared();
- }
+ mPipMenuController.attach(leash);
+
if (mShouldIgnoreEnteringPipTransition) {
final Rect destinationBounds = mPipBoundsState.getBounds();
@@ -674,9 +672,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPictureInPictureParams = null;
mState = State.UNDEFINED;
mPipUiEventLoggerLogger.setTaskInfo(null);
- if (mMenuActivityController != null) {
- mMenuActivityController.onTaskVanished();
- }
+ mPipMenuController.detach();
}
@Override
@@ -819,6 +815,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
}
+ /**
+ * Animates resizing of the pinned stack given the duration and start bounds.
+ * This is used when the starting bounds is not the current PiP bounds.
+ */
+ public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
+ Consumer<Rect> updateBoundsCallback) {
+ if (mShouldDeferEnteringPip) {
+ Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
+ return;
+ }
+ scheduleAnimateResizePip(fromBounds, toBounds, null /* sourceHintRect */,
+ TRANSITION_DIRECTION_SNAP_AFTER_RESIZE, duration, updateBoundsCallback);
+ }
+
private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
int durationMs, Consumer<Rect> updateBoundsCallback) {
@@ -956,9 +966,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
.round(tx, mLeash, mState.isInPip());
- if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) {
+ if (mPipMenuController.isMenuVisible()) {
runOnMainHandler(() ->
- mMenuActivityController.resizePipMenu(mLeash, tx, destinationBounds));
+ mPipMenuController.resizePipMenu(mLeash, tx, destinationBounds));
} else {
tx.apply();
}
@@ -982,9 +992,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
- if (mMenuActivityController != null && mMenuActivityController.isMenuVisible()) {
+ if (mPipMenuController.isMenuVisible()) {
runOnMainHandler(() ->
- mMenuActivityController.movePipMenu(mLeash, tx, destinationBounds));
+ mPipMenuController.movePipMenu(mLeash, tx, destinationBounds));
} else {
tx.apply();
}
@@ -1001,8 +1011,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
return;
- } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA
- && mMenuActivityController != null) {
+ } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
// TODO: Synchronize this correctly in #applyEnterPipSyncTransaction
finishResizeForMenu(destinationBounds);
return;
@@ -1015,13 +1024,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
private void finishResizeForMenu(Rect destinationBounds) {
- if (mMenuActivityController == null) {
- if (DEBUG) Log.d(TAG, "mMenuActivityController is null");
- return;
- }
runOnMainHandler(() -> {
- mMenuActivityController.movePipMenu(null, null, destinationBounds);
- mMenuActivityController.updateMenuBounds(destinationBounds);
+ mPipMenuController.movePipMenu(null, null, destinationBounds);
+ mPipMenuController.updateMenuBounds(destinationBounds);
});
}
@@ -1083,8 +1088,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Log.w(TAG, "Abort animation, invalid leash");
return;
}
+ Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
+ ? mPipBoundsState.getBounds() : currentBounds;
mPipAnimationController
- .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect, direction)
+ .getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect,
+ direction)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index d4217553ef2d..5db8f3d7ef40 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -18,12 +18,6 @@ package com.android.wm.shell.pip.phone;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
import android.annotation.Nullable;
@@ -33,7 +27,6 @@ import android.app.RemoteAction;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.graphics.Matrix;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Debug;
@@ -44,27 +37,26 @@ import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
+import com.android.wm.shell.pip.PipMenuController;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
- * Manages the PiP menu activity which can show menu options or a scrim.
+ * Manages the PiP menu view which can show menu options or a scrim.
*
* The current media session provides actions whenever there are no valid actions provided by the
* current PiP activity. Otherwise, those actions always take precedence.
*/
-public class PipMenuActivityController {
+public class PhonePipMenuController implements PipMenuController {
private static final String TAG = "PipMenuActController";
- private static final String MENU_WINDOW_TITLE = "PipMenuView";
private static final boolean DEBUG = false;
public static final int MENU_STATE_NONE = 0;
@@ -124,7 +116,7 @@ public class PipMenuActivityController {
}
};
- public PipMenuActivityController(Context context,
+ public PhonePipMenuController(Context context,
PipMediaController mediaController, SystemWindows systemWindows) {
mContext = context;
mMediaController = mediaController;
@@ -138,20 +130,22 @@ public class PipMenuActivityController {
/**
* Attach the menu when the PiP task first appears.
*/
- public void onTaskAppeared() {
+ @Override
+ public void attach(SurfaceControl leash) {
attachPipMenuView();
}
/**
* Detach the menu when the PiP task is gone.
*/
- public void onTaskVanished() {
+ @Override
+ public void detach() {
hideMenu();
detachPipMenuView();
}
- public void onPinnedStackAnimationEnded() {
+ void onPinnedStackAnimationEnded() {
if (isMenuVisible()) {
mPipMenuView.onPipAnimationEnded();
}
@@ -163,7 +157,9 @@ public class PipMenuActivityController {
detachPipMenuView();
}
mPipMenuView = new PipMenuView(mContext, this);
- mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(0, 0), 0, SHELL_ROOT_LAYER_PIP);
+ mSystemWindows.addView(mPipMenuView,
+ getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
+ 0, SHELL_ROOT_LAYER_PIP);
}
private void detachPipMenuView() {
@@ -181,9 +177,11 @@ public class PipMenuActivityController {
* Updates the layout parameters of the menu.
* @param destinationBounds New Menu bounds.
*/
+ @Override
public void updateMenuBounds(Rect destinationBounds) {
mSystemWindows.updateViewLayout(mPipMenuView,
- getPipMenuLayoutParams(destinationBounds.width(), destinationBounds.height()));
+ getPipMenuLayoutParams(MENU_WINDOW_TITLE, destinationBounds.width(),
+ destinationBounds.height()));
}
/**
@@ -206,6 +204,16 @@ public class PipMenuActivityController {
}
/**
+ * When other components requests the menu controller directly to show the menu, we must
+ * first fire off the request to the other listeners who will then propagate the call
+ * back to the controller with the right parameters.
+ */
+ @Override
+ public void showMenu() {
+ mListeners.forEach(Listener::onPipShowMenu);
+ }
+
+ /**
* Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu
* upon PiP window transition is finished.
*/
@@ -250,6 +258,7 @@ public class PipMenuActivityController {
/**
* Move the PiP menu, which does a translation and possibly a scale transformation.
*/
+ @Override
public void movePipMenu(@Nullable SurfaceControl pipLeash,
@Nullable SurfaceControl.Transaction t,
Rect destinationBounds) {
@@ -290,6 +299,7 @@ public class PipMenuActivityController {
/**
* Does an immediate window crop of the PiP menu.
*/
+ @Override
public void resizePipMenu(@Nullable SurfaceControl pipLeash,
@Nullable SurfaceControl.Transaction t,
Rect destinationBounds) {
@@ -391,8 +401,9 @@ public class PipMenuActivityController {
}
/**
- * Sets the menu actions to the actions provided by the current PiP activity.
+ * Sets the menu actions to the actions provided by the current PiP menu.
*/
+ @Override
public void setAppActions(ParceledListSlice<RemoteAction> appActions) {
mAppActions = appActions;
updateMenuActions();
@@ -406,10 +417,6 @@ public class PipMenuActivityController {
mListeners.forEach(Listener::onPipDismiss);
}
- void onPipShowMenu() {
- mListeners.forEach(Listener::onPipShowMenu);
- }
-
/**
* @return the best set of actions to show in the PiP menu.
*/
@@ -421,21 +428,6 @@ public class PipMenuActivityController {
}
/**
- * Returns a default LayoutParams for the PIP Menu.
- * @param width the PIP stack width.
- * @param height the PIP stack height.
- */
- public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) {
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
- TYPE_APPLICATION_OVERLAY,
- FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY | FLAG_NOT_TOUCHABLE,
- PixelFormat.TRANSLUCENT);
- lp.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY;
- lp.setTitle(MENU_WINDOW_TITLE);
- return lp;
- }
-
- /**
* Updates the PiP menu with the best set of actions provided.
*/
private void updateMenuActions() {
@@ -521,7 +513,7 @@ public class PipMenuActivityController {
}
}
- public void dump(PrintWriter pw, String prefix) {
+ void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
pw.println(innerPrefix + "mMenuState=" + mMenuState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index f153aa5a1beb..7194fc70025c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -20,15 +20,18 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
-import android.os.Handler;
import android.os.RemoteException;
import android.view.MagnificationSpec;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import androidx.annotation.BinderThread;
+
import com.android.wm.shell.R;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -40,8 +43,7 @@ import java.util.List;
* Expose the touch actions to accessibility as if this object were a window with a single view.
* That pseudo-view exposes all of the actions this object can perform.
*/
-public class PipAccessibilityInteractionConnection
- extends IAccessibilityInteractionConnection.Stub {
+public class PipAccessibilityInteractionConnection {
public interface AccessibilityCallbacks {
void onAccessibilityShowMenu();
@@ -50,14 +52,15 @@ public class PipAccessibilityInteractionConnection
private static final long ACCESSIBILITY_NODE_ID = 1;
private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
- private Context mContext;
- private Handler mHandler;
+ private final Context mContext;
+ private final ShellExecutor mShellMainExcutor;
private final @NonNull PipBoundsState mPipBoundsState;
- private PipMotionHelper mMotionHelper;
- private PipTaskOrganizer mTaskOrganizer;
- private PipSnapAlgorithm mSnapAlgorithm;
- private Runnable mUpdateMovementBoundCallback;
- private AccessibilityCallbacks mCallbacks;
+ private final PipMotionHelper mMotionHelper;
+ private final PipTaskOrganizer mTaskOrganizer;
+ private final PipSnapAlgorithm mSnapAlgorithm;
+ private final Runnable mUpdateMovementBoundCallback;
+ private final AccessibilityCallbacks mCallbacks;
+ private final IAccessibilityInteractionConnection mConnectionImpl;
private final Rect mNormalBounds = new Rect();
private final Rect mExpandedBounds = new Rect();
@@ -69,19 +72,23 @@ public class PipAccessibilityInteractionConnection
@NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
- Handler handler) {
+ ShellExecutor shellMainExcutor) {
mContext = context;
- mHandler = handler;
+ mShellMainExcutor = shellMainExcutor;
mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
mTaskOrganizer = taskOrganizer;
mSnapAlgorithm = snapAlgorithm;
mUpdateMovementBoundCallback = updateMovementBoundCallback;
mCallbacks = callbacks;
+ mConnectionImpl = new PipAccessibilityInteractionConnectionImpl();
+ }
+
+ public void register(AccessibilityManager am) {
+ am.setPictureInPictureActionReplacingConnection(mConnectionImpl);
}
- @Override
- public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
+ private void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle args) {
@@ -94,8 +101,7 @@ public class PipAccessibilityInteractionConnection
}
}
- @Override
- public void performAccessibilityAction(long accessibilityNodeId, int action,
+ private void performAccessibilityAction(long accessibilityNodeId, int action,
Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) {
@@ -115,9 +121,7 @@ public class PipAccessibilityInteractionConnection
} else {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
- mHandler.post(() -> {
- mCallbacks.onAccessibilityShowMenu();
- });
+ mCallbacks.onAccessibilityShowMenu();
result = true;
break;
case AccessibilityNodeInfo.ACTION_DISMISS:
@@ -172,8 +176,7 @@ public class PipAccessibilityInteractionConnection
});
}
- @Override
- public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
+ private void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
String viewId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
@@ -185,8 +188,7 @@ public class PipAccessibilityInteractionConnection
}
}
- @Override
- public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
+ private void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
@@ -198,8 +200,7 @@ public class PipAccessibilityInteractionConnection
}
}
- @Override
- public void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
+ private void findFocus(long accessibilityNodeId, int focusType, Region interactiveRegion,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
// We have no view that can take focus
@@ -210,8 +211,7 @@ public class PipAccessibilityInteractionConnection
}
}
- @Override
- public void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
+ private void focusSearch(long accessibilityNodeId, int direction, Region interactiveRegion,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
// We have no view that can take focus
@@ -222,16 +222,6 @@ public class PipAccessibilityInteractionConnection
}
}
- @Override
- public void clearAccessibilityFocus() {
- // We should not be here.
- }
-
- @Override
- public void notifyOutsideTouch() {
- // Do nothing.
- }
-
/**
* Update the normal and expanded bounds so they can be used for Resize.
*/
@@ -271,4 +261,95 @@ public class PipAccessibilityInteractionConnection
mAccessibilityNodeInfoList.add(info);
return mAccessibilityNodeInfoList;
}
+
+ @BinderThread
+ private class PipAccessibilityInteractionConnectionImpl
+ extends IAccessibilityInteractionConnection.Stub {
+ @Override
+ public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
+ Region bounds, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
+ Bundle arguments) throws RemoteException {
+ mShellMainExcutor.execute(() -> {
+ PipAccessibilityInteractionConnection.this
+ .findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds,
+ interactionId, callback, flags, interrogatingPid, interrogatingTid,
+ spec, arguments);
+ });
+ }
+
+ @Override
+ public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId,
+ Region bounds, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+ throws RemoteException {
+ mShellMainExcutor.execute(() -> {
+ PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId(
+ accessibilityNodeId, viewId, bounds, interactionId, callback, flags,
+ interrogatingPid, interrogatingTid, spec);
+ });
+ }
+
+ @Override
+ public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
+ Region bounds, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+ throws RemoteException {
+ mShellMainExcutor.execute(() -> {
+ PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText(
+ accessibilityNodeId, text, bounds, interactionId, callback, flags,
+ interrogatingPid, interrogatingTid, spec);
+ });
+ }
+
+ @Override
+ public void findFocus(long accessibilityNodeId, int focusType, Region bounds,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+ throws RemoteException {
+ mShellMainExcutor.execute(() -> {
+ PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType,
+ bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
+ spec);
+ });
+ }
+
+ @Override
+ public void focusSearch(long accessibilityNodeId, int direction, Region bounds,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
+ throws RemoteException {
+ mShellMainExcutor.execute(() -> {
+ PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId,
+ direction,
+ bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
+ spec);
+ });
+ }
+
+ @Override
+ public void performAccessibilityAction(long accessibilityNodeId, int action,
+ Bundle arguments, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid) throws RemoteException {
+ mShellMainExcutor.execute(() -> {
+ PipAccessibilityInteractionConnection.this.performAccessibilityAction(
+ accessibilityNodeId, action, arguments, interactionId, callback, flags,
+ interrogatingPid, interrogatingTid);
+ });
+ }
+
+ @Override
+ public void clearAccessibilityFocus() throws RemoteException {
+ // Do nothing
+ }
+
+ @Override
+ public void notifyOutsideTouch() throws RemoteException {
+ // Do nothing
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 598b5d9b5d30..3234ef6ccf66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -41,7 +41,6 @@ import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.view.DisplayInfo;
-import android.view.IPinnedStackController;
import android.view.WindowManagerGlobal;
import android.window.WindowContainerTransaction;
@@ -52,7 +51,6 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.PipInputConsumer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -92,7 +90,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private boolean mIsInFixedRotation;
private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
- protected PipMenuActivityController mMenuController;
+ protected PhonePipMenuController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer;
protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener =
new PipControllerPinnedStackListener();
@@ -163,63 +161,50 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private class PipControllerPinnedStackListener extends
PinnedStackListenerForwarder.PinnedStackListener {
@Override
- public void onListenerRegistered(IPinnedStackController controller) {
- mMainExecutor.execute(() -> mTouchHandler.setPinnedStackController(controller));
- }
-
- @Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- mMainExecutor.execute(() -> {
- mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
- mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
- });
+ mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
+ mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
}
@Override
public void onMovementBoundsChanged(boolean fromImeAdjustment) {
- mMainExecutor.execute(() -> updateMovementBounds(null /* toBounds */,
+ updateMovementBounds(null /* toBounds */,
false /* fromRotation */, fromImeAdjustment, false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */));
+ null /* windowContainerTransaction */);
}
@Override
public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
- mMainExecutor.execute(() -> mMenuController.setAppActions(actions));
+ mMenuController.setAppActions(actions);
}
@Override
public void onActivityHidden(ComponentName componentName) {
- mMainExecutor.execute(() -> {
- if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
- // The activity was removed, we don't want to restore to the reentry state
- // saved for this component anymore.
- mPipBoundsState.setLastPipComponentName(null);
- }
- });
+ if (componentName.equals(mPipBoundsState.getLastPipComponentName())) {
+ // The activity was removed, we don't want to restore to the reentry state
+ // saved for this component anymore.
+ mPipBoundsState.setLastPipComponentName(null);
+ }
}
@Override
public void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mMainExecutor.execute(() -> mPipBoundsState.setDisplayInfo(displayInfo));
+ mPipBoundsState.setDisplayInfo(displayInfo);
}
@Override
public void onConfigurationChanged() {
- mMainExecutor.execute(() -> {
- mPipBoundsAlgorithm.onConfigurationChanged(mContext);
- mTouchHandler.onConfigurationChanged();
- mPipBoundsState.onConfigurationChanged();
- });
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ mTouchHandler.onConfigurationChanged();
+ mPipBoundsState.onConfigurationChanged();
}
@Override
public void onAspectRatioChanged(float aspectRatio) {
// TODO(b/169373982): Remove this callback as it is redundant with PipTaskOrg params
// change.
- mMainExecutor.execute(() -> {
- mPipBoundsState.setAspectRatio(aspectRatio);
- mTouchHandler.onAspectRatioChanged();
- });
+ mPipBoundsState.setAspectRatio(aspectRatio);
+ mTouchHandler.onAspectRatioChanged();
}
}
@@ -229,7 +214,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
PipMediaController pipMediaController,
- PipMenuActivityController pipMenuActivityController,
+ PhonePipMenuController phonePipMenuController,
PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -250,7 +235,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mPipTaskOrganizer = pipTaskOrganizer;
mMainExecutor = mainExecutor;
mMediaController = pipMediaController;
- mMenuController = pipMenuActivityController;
+ mMenuController = phonePipMenuController;
mTouchHandler = pipTouchHandler;
mAppOpsListener = pipAppOpsListener;
mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
@@ -632,7 +617,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
public static PipController create(Context context, DisplayController displayController,
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
- PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer,
+ PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
@@ -641,7 +626,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
- pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer,
+ pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer,
pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index 87ddb181dcce..0c64c8ca9b64 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.common;
+package com.android.wm.shell.pip.phone;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
-import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import android.os.Binder;
import android.os.IBinder;
@@ -30,7 +28,6 @@ import android.view.Choreographer;
import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputEvent;
-import android.view.WindowManagerGlobal;
import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 6e3cd8e9aa09..2e10fc93cafb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -23,9 +23,9 @@ import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTR
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
-import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
-import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
+import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
+import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
+import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -105,7 +105,7 @@ public class PipMenuView extends FrameLayout {
private int mBetweenActionPaddingLand;
private AnimatorSet mMenuContainerAnimator;
- private PipMenuActivityController mController;
+ private PhonePipMenuController mController;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -127,7 +127,7 @@ public class PipMenuView extends FrameLayout {
protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
- public PipMenuView(Context context, PipMenuActivityController controller) {
+ public PipMenuView(Context context, PhonePipMenuController controller) {
super(context, null, 0);
mContext = context;
mController = controller;
@@ -182,7 +182,7 @@ public class PipMenuView extends FrameLayout {
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
- mController.onPipShowMenu();
+ mController.showMenu();
}
return super.performAccessibilityAction(host, action, args);
}
@@ -475,7 +475,7 @@ public class PipMenuView extends FrameLayout {
final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(settingsIntent, UserHandle.CURRENT);
+ mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 903f7d773896..e32d3b955081 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -70,7 +70,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private final PipTaskOrganizer mPipTaskOrganizer;
private @NonNull PipBoundsState mPipBoundsState;
- private PipMenuActivityController mMenuController;
+ private PhonePipMenuController mMenuController;
private PipSnapAlgorithm mSnapAlgorithm;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@@ -162,7 +162,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
};
public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
- PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
+ PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController,
PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) {
mContext = context;
mPipTaskOrganizer = pipTaskOrganizer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
new file mode 100644
index 000000000000..28cbe35745a9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.pip.phone;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+
+/**
+ * Helper class to calculate the new size given two-fingers pinch to resize.
+ */
+public class PipPinchResizingAlgorithm {
+ private static final Rect TMP_RECT = new Rect();
+ /**
+ * Given inputs and requirements and current PiP bounds, return the new size.
+ *
+ * @param x0 x-coordinate of the primary input.
+ * @param y0 y-coordinate of the primary input.
+ * @param x1 x-coordinate of the secondary input.
+ * @param y1 y-coordinate of the secondary input.
+ * @param downx0 x-coordinate of the original down point of the primary input.
+ * @param downy0 y-coordinate of the original down ponit of the primary input.
+ * @param downx1 x-coordinate of the original down point of the secondary input.
+ * @param downy1 y-coordinate of the original down point of the secondary input.
+ * @param currentPipBounds current PiP bounds.
+ * @param minVisibleWidth minimum visible width.
+ * @param minVisibleHeight minimum visible height.
+ * @param maxSize max size.
+ * @return The new resized PiP bounds, sharing the same center.
+ */
+ public static Rect pinchResize(float x0, float y0, float x1, float y1,
+ float downx0, float downy0, float downx1, float downy1, Rect currentPipBounds,
+ int minVisibleWidth, int minVisibleHeight, Point maxSize) {
+
+ int width = currentPipBounds.width();
+ int height = currentPipBounds.height();
+ int left = currentPipBounds.left;
+ int top = currentPipBounds.top;
+ int right = currentPipBounds.right;
+ int bottom = currentPipBounds.bottom;
+ final float aspect = (float) width / (float) height;
+ final int widthDelta = Math.round(Math.abs(x0 - x1) - Math.abs(downx0 - downx1));
+ final int heightDelta = Math.round(Math.abs(y0 - y1) - Math.abs(downy0 - downy1));
+
+ width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x));
+ height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y));
+
+ // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
+ // drag axis. What ever is producing the bigger rectangle will be chosen.
+ int width1;
+ int width2;
+ int height1;
+ int height2;
+ if (aspect > 1.0f) {
+ // Assuming that the width is our target we calculate the height.
+ width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
+ height1 = Math.round((float) width1 / aspect);
+ if (height1 < minVisibleHeight) {
+ // If the resulting height is too small we adjust to the minimal size.
+ height1 = minVisibleHeight;
+ width1 = Math.max(minVisibleWidth,
+ Math.min(maxSize.x, Math.round((float) height1 * aspect)));
+ }
+ // Assuming that the height is our target we calculate the width.
+ height2 = Math.max(minVisibleHeight, Math.min(maxSize.y, height));
+ width2 = Math.round((float) height2 * aspect);
+ if (width2 < minVisibleWidth) {
+ // If the resulting width is too small we adjust to the minimal size.
+ width2 = minVisibleWidth;
+ height2 = Math.max(minVisibleHeight,
+ Math.min(maxSize.y, Math.round((float) width2 / aspect)));
+ }
+ } else {
+ // Assuming that the width is our target we calculate the height.
+ width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
+ height1 = Math.round((float) width1 * aspect);
+ if (height1 < minVisibleHeight) {
+ // If the resulting height is too small we adjust to the minimal size.
+ height1 = minVisibleHeight;
+ width1 = Math.max(minVisibleWidth,
+ Math.min(maxSize.x, Math.round((float) height1 / aspect)));
+ }
+ // Assuming that the height is our target we calculate the width.
+ height2 = Math.max(minVisibleHeight, Math.min(maxSize.y, height));
+ width2 = Math.round((float) height2 / aspect);
+ if (width2 < minVisibleWidth) {
+ // If the resulting width is too small we adjust to the minimal size.
+ width2 = minVisibleWidth;
+ height2 = Math.max(minVisibleHeight,
+ Math.min(maxSize.y, Math.round((float) width2 * aspect)));
+ }
+ }
+
+ // Use the bigger of the two rectangles if the major change was positive, otherwise
+ // do the opposite.
+ final boolean grows = width > (right - left) || height > (bottom - top);
+ if (grows == (width1 * height1 > width2 * height2)) {
+ width = width1;
+ height = height1;
+ } else {
+ width = width2;
+ height = height2;
+ }
+
+ TMP_RECT.set(currentPipBounds.centerX() - width / 2,
+ currentPipBounds.centerY() - height / 2,
+ currentPipBounds.centerX() + width / 2,
+ currentPipBounds.centerY() + height / 2);
+ return TMP_RECT;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index e5a49c430d82..88a11689b90b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -39,7 +39,6 @@ import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputMonitor;
import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;
import androidx.annotation.VisibleForTesting;
@@ -62,19 +61,21 @@ import java.util.function.Function;
public class PipResizeGestureHandler {
private static final String TAG = "PipResizeGestureHandler";
- private static final float PINCH_THRESHOLD = 0.05f;
- private static final float STARTING_SCALE_FACTOR = 1.0f;
+ private static final int PINCH_RESIZE_SNAP_DURATION = 250;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final PipMotionHelper mMotionHelper;
private final PipBoundsState mPipBoundsState;
+ private final PipTaskOrganizer mPipTaskOrganizer;
+ private final PhonePipMenuController mPhonePipMenuController;
+ private final PipUiEventLogger mPipUiEventLogger;
private final int mDisplayId;
private final Executor mMainExecutor;
- private final ScaleGestureDetector mScaleGestureDetector;
private final Region mTmpRegion = new Region();
private final PointF mDownPoint = new PointF();
+ private final PointF mDownSecondaryPoint = new PointF();
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
private final Rect mLastResizeBounds = new Rect();
@@ -88,6 +89,7 @@ public class PipResizeGestureHandler {
private final Rect mDisplayBounds = new Rect();
private final Function<Rect, Rect> mMovementBoundsSupplier;
private final Runnable mUpdateMovementBoundsRunnable;
+ private final Handler mHandler;
private int mDelta;
private float mTouchSlop;
@@ -96,15 +98,17 @@ public class PipResizeGestureHandler {
private boolean mIsEnabled;
private boolean mEnablePinchResize;
private boolean mIsSysUiStateValid;
+ // For drag-resize
private boolean mThresholdCrossed;
+ // For pinch-resize
+ private boolean mThresholdCrossed0;
+ private boolean mThresholdCrossed1;
private boolean mUsingPinchToZoom = false;
- private float mScaleFactor = STARTING_SCALE_FACTOR;
+ int mFirstIndex = -1;
+ int mSecondIndex = -1;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
- private PipTaskOrganizer mPipTaskOrganizer;
- private PipMenuActivityController mPipMenuActivityController;
- private PipUiEventLogger mPipUiEventLogger;
private int mCtrlType;
@@ -112,7 +116,7 @@ public class PipResizeGestureHandler {
PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger,
- PipMenuActivityController menuActivityController) {
+ PhonePipMenuController menuActivityController) {
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
@@ -122,68 +126,13 @@ public class PipResizeGestureHandler {
mPipTaskOrganizer = pipTaskOrganizer;
mMovementBoundsSupplier = movementBoundsSupplier;
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
- mPipMenuActivityController = menuActivityController;
+ mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
+ mHandler = new Handler(Looper.getMainLooper());
context.getDisplay().getRealSize(mMaxSize);
reloadResources();
- mScaleGestureDetector = new ScaleGestureDetector(context,
- new ScaleGestureDetector.OnScaleGestureListener() {
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- mScaleFactor *= detector.getScaleFactor();
-
- if (!mThresholdCrossed
- && (mScaleFactor > (STARTING_SCALE_FACTOR + PINCH_THRESHOLD)
- || mScaleFactor < (STARTING_SCALE_FACTOR - PINCH_THRESHOLD))) {
- mThresholdCrossed = true;
- mInputMonitor.pilferPointers();
- }
- if (mThresholdCrossed) {
- int height = Math.min(mMaxSize.y, Math.max(mMinSize.y,
- (int) (mScaleFactor * mLastDownBounds.height())));
- int width = Math.min(mMaxSize.x, Math.max(mMinSize.x,
- (int) (mScaleFactor * mLastDownBounds.width())));
- int top, bottom, left, right;
-
- if ((mCtrlType & CTRL_TOP) != 0) {
- top = mLastDownBounds.bottom - height;
- bottom = mLastDownBounds.bottom;
- } else {
- top = mLastDownBounds.top;
- bottom = mLastDownBounds.top + height;
- }
-
- if ((mCtrlType & CTRL_LEFT) != 0) {
- left = mLastDownBounds.right - width;
- right = mLastDownBounds.right;
- } else {
- left = mLastDownBounds.left;
- right = mLastDownBounds.left + width;
- }
-
- mLastResizeBounds.set(left, top, right, bottom);
- mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds,
- mLastResizeBounds,
- null);
- }
- return true;
- }
-
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- setCtrlTypeForPinchToZoom();
- return true;
- }
-
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- mScaleFactor = STARTING_SCALE_FACTOR;
- finishResize();
- }
- });
-
mEnablePinchResize = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_PINCH_RESIZE,
@@ -274,7 +223,7 @@ public class PipResizeGestureHandler {
if (ev instanceof MotionEvent) {
if (mUsingPinchToZoom) {
- mScaleGestureDetector.onTouchEvent((MotionEvent) ev);
+ onPinchResize((MotionEvent) ev);
} else {
onDragCornerResize((MotionEvent) ev);
}
@@ -282,6 +231,13 @@ public class PipResizeGestureHandler {
}
/**
+ * Checks if there is currently an on-going gesture, either drag-resize or pinch-resize.
+ */
+ public boolean hasOngoingGesture() {
+ return mCtrlType != CTRL_NONE || mUsingPinchToZoom;
+ }
+
+ /**
* Check whether the current x,y coordinate is within the region in which drag-resize should
* start.
* This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which
@@ -295,7 +251,7 @@ public class PipResizeGestureHandler {
* |_|_|_________|_|_|
* |_|_| |_|_|
*/
- public boolean isWithinTouchRegion(int x, int y) {
+ public boolean isWithinDragResizeRegion(int x, int y) {
final Rect currentPipBounds = mPipBoundsState.getBounds();
if (currentPipBounds == null) {
return false;
@@ -327,15 +283,14 @@ public class PipResizeGestureHandler {
if (isInValidSysUiState()) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- // Always pass the DOWN event to the ScaleGestureDetector
- mScaleGestureDetector.onTouchEvent(ev);
- if (isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
+ if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) {
return true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mEnablePinchResize && ev.getPointerCount() == 2) {
+ onPinchResize(ev);
mUsingPinchToZoom = true;
return true;
}
@@ -348,33 +303,11 @@ public class PipResizeGestureHandler {
return false;
}
- private void setCtrlTypeForPinchToZoom() {
- final Rect currentPipBounds = mPipBoundsState.getBounds();
- mLastDownBounds.set(mPipBoundsState.getBounds());
-
- Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
- mDisplayBounds.set(movementBounds.left,
- movementBounds.top,
- movementBounds.right + currentPipBounds.width(),
- movementBounds.bottom + currentPipBounds.height());
-
- if (currentPipBounds.left == mDisplayBounds.left) {
- mCtrlType |= CTRL_RIGHT;
- } else {
- mCtrlType |= CTRL_LEFT;
- }
-
- if (currentPipBounds.top > mDisplayBounds.top + mDisplayBounds.height()) {
- mCtrlType |= CTRL_TOP;
- } else {
- mCtrlType |= CTRL_BOTTOM;
- }
- }
-
private void setCtrlType(int x, int y) {
final Rect currentPipBounds = mPipBoundsState.getBounds();
Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
+
mDisplayBounds.set(movementBounds.left,
movementBounds.top,
movementBounds.right + currentPipBounds.width(),
@@ -408,6 +341,78 @@ public class PipResizeGestureHandler {
return mIsSysUiStateValid;
}
+ private void onPinchResize(MotionEvent ev) {
+ int action = ev.getActionMasked();
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mFirstIndex = -1;
+ mSecondIndex = -1;
+ finishResize();
+ }
+
+ if (ev.getPointerCount() != 2) {
+ return;
+ }
+
+ if (action == MotionEvent.ACTION_POINTER_DOWN) {
+ if (mFirstIndex == -1 && mSecondIndex == -1) {
+ mFirstIndex = 0;
+ mSecondIndex = 1;
+ mLastResizeBounds.setEmpty();
+ mDownPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex));
+ mDownSecondaryPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex));
+
+ mLastResizeBounds.setEmpty();
+ mLastDownBounds.set(mPipBoundsState.getBounds());
+ }
+ }
+
+ if (action == MotionEvent.ACTION_MOVE) {
+ if (mFirstIndex == -1 || mSecondIndex == -1) {
+ return;
+ }
+
+ float x0 = ev.getRawX(mFirstIndex);
+ float y0 = ev.getRawY(mFirstIndex);
+ float x1 = ev.getRawX(mSecondIndex);
+ float y1 = ev.getRawY(mSecondIndex);
+
+ double hypot0 = Math.hypot(x0 - mDownPoint.x, y0 - mDownPoint.y);
+ double hypot1 = Math.hypot(x1 - mDownSecondaryPoint.x, y1 - mDownSecondaryPoint.y);
+ // Capture inputs
+ if (hypot0 > mTouchSlop && !mThresholdCrossed0) {
+ mInputMonitor.pilferPointers();
+ mThresholdCrossed0 = true;
+ // Reset the down to begin resizing from this point
+ mDownPoint.set(x0, y0);
+ }
+ if (hypot1 > mTouchSlop && !mThresholdCrossed1) {
+ mInputMonitor.pilferPointers();
+ mThresholdCrossed1 = true;
+ // Reset the down to begin resizing from this point
+ mDownSecondaryPoint.set(x1, y1);
+ }
+ if (mThresholdCrossed0 || mThresholdCrossed1) {
+ if (mPhonePipMenuController.isMenuVisible()) {
+ mPhonePipMenuController.hideMenu();
+ }
+
+ x0 = mThresholdCrossed0 ? x0 : mDownPoint.x;
+ y0 = mThresholdCrossed0 ? y0 : mDownPoint.y;
+ x1 = mThresholdCrossed1 ? x1 : mDownSecondaryPoint.x;
+ y1 = mThresholdCrossed1 ? y1 : mDownSecondaryPoint.y;
+
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+ mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
+ mDownPoint.x, mDownPoint.y, mDownSecondaryPoint.x, mDownSecondaryPoint.y,
+ currentPipBounds, mMinSize.x, mMinSize.y, mMaxSize));
+
+ mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
+ null);
+ }
+ }
+ }
+
private void onDragCornerResize(MotionEvent ev) {
int action = ev.getActionMasked();
float x = ev.getX();
@@ -415,15 +420,15 @@ public class PipResizeGestureHandler {
if (action == MotionEvent.ACTION_DOWN) {
final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastResizeBounds.setEmpty();
- mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y);
+ mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y);
if (mAllowGesture) {
setCtrlType((int) x, (int) y);
mDownPoint.set(x, y);
mLastDownBounds.set(mPipBoundsState.getBounds());
}
if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
- && mPipMenuActivityController.isMenuVisible()) {
- mPipMenuActivityController.hideMenu();
+ && mPhonePipMenuController.isMenuVisible()) {
+ mPhonePipMenuController.hideMenu();
}
} else if (mAllowGesture) {
@@ -442,9 +447,9 @@ public class PipResizeGestureHandler {
mInputMonitor.pilferPointers();
}
if (mThresholdCrossed) {
- if (mPipMenuActivityController.isMenuVisible()) {
- mPipMenuActivityController.hideMenuWithoutResize();
- mPipMenuActivityController.hideMenu();
+ if (mPhonePipMenuController.isMenuVisible()) {
+ mPhonePipMenuController.hideMenuWithoutResize();
+ mPhonePipMenuController.hideMenu();
}
final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
@@ -468,15 +473,30 @@ public class PipResizeGestureHandler {
private void finishResize() {
if (!mLastResizeBounds.isEmpty()) {
- mUserResizeBounds.set(mLastResizeBounds);
- mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
- (Rect bounds) -> {
- new Handler(Looper.getMainLooper()).post(() -> {
- mMotionHelper.synchronizePinnedStackBounds();
- mUpdateMovementBoundsRunnable.run();
- resetState();
+ final Runnable callback = () -> {
+ mUserResizeBounds.set(mLastResizeBounds);
+ mMotionHelper.synchronizePinnedStackBounds();
+ mUpdateMovementBoundsRunnable.run();
+ resetState();
+ };
+
+ // Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped
+ // position correctly. Drag-resize does not need to move, so just finalize resize.
+ if (mUsingPinchToZoom) {
+ final Rect startBounds = new Rect(mLastResizeBounds);
+ mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
+ mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
+ mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
+ PINCH_RESIZE_SNAP_DURATION,
+ (Rect rect) -> {
+ mHandler.post(callback);
});
- });
+ } else {
+ mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
+ (Rect bounds) -> {
+ mHandler.post(callback);
+ });
+ }
mPipUiEventLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index a78c4ecdb39f..9281f58f522f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -18,9 +18,9 @@ package com.android.wm.shell.pip.phone;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
-import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
-import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
-import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
+import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_CLOSE;
+import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
+import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -31,11 +31,8 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.RemoteException;
import android.provider.DeviceConfig;
-import android.util.Log;
import android.util.Size;
-import android.view.IPinnedStackController;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -47,6 +44,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -75,10 +73,9 @@ public class PipTouchHandler {
private final PipDismissTargetHandler mPipDismissTargetHandler;
private PipResizeGestureHandler mPipResizeGestureHandler;
- private IPinnedStackController mPinnedStackController;
private WeakReference<Consumer<Rect>> mPipExclusionBoundsChangeListener;
- private final PipMenuActivityController mMenuController;
+ private final PhonePipMenuController mMenuController;
private final AccessibilityManager mAccessibilityManager;
private boolean mShowPipMenuOnAnimationEnd = false;
@@ -125,7 +122,7 @@ public class PipTouchHandler {
/**
* A listener for the PIP menu activity.
*/
- private class PipMenuListener implements PipMenuActivityController.Listener {
+ private class PipMenuListener implements PhonePipMenuController.Listener {
@Override
public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) {
setMenuState(menuState, resize, callback);
@@ -152,12 +149,13 @@ public class PipTouchHandler {
@SuppressLint("InflateParams")
public PipTouchHandler(Context context,
- PipMenuActivityController menuController,
+ PhonePipMenuController menuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
- PipUiEventLogger pipUiEventLogger) {
+ PipUiEventLogger pipUiEventLogger,
+ ShellExecutor shellMainExecutor) {
// Initialize the Pip input consumer
mContext = context;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -188,7 +186,7 @@ public class PipTouchHandler {
mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
- this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler);
+ this::onAccessibilityShowMenu, this::updateMovementBounds, shellMainExecutor);
mPipUiEventLogger = pipUiEventLogger;
@@ -436,8 +434,11 @@ public class PipTouchHandler {
* TODO Add appropriate description
*/
public void onRegistrationChanged(boolean isRegistered) {
- mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
- ? mConnection : null);
+ if (isRegistered) {
+ mConnection.register(mAccessibilityManager);
+ } else {
+ mAccessibilityManager.setPictureInPictureActionReplacingConnection(null);
+ }
if (!isRegistered && mTouchState.isUserInteracting()) {
// If the input consumer is unregistered while the user is interacting, then we may not
// get the final TOUCH_UP event, so clean up the dismiss target as well
@@ -459,10 +460,6 @@ public class PipTouchHandler {
if (!(inputEvent instanceof MotionEvent)) {
return true;
}
- // Skip touch handling until we are bound to the controller
- if (mPinnedStackController == null) {
- return true;
- }
MotionEvent ev = (MotionEvent) inputEvent;
if (!mPipBoundsState.isStashed() && mPipResizeGestureHandler.willStartResizeGesture(ev)) {
@@ -473,6 +470,11 @@ public class PipTouchHandler {
return true;
}
+ if (mPipResizeGestureHandler.hasOngoingGesture()) {
+ mPipDismissTargetHandler.hideDismissTargetMaybe();
+ return true;
+ }
+
if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
&& mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
// If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
@@ -586,13 +588,6 @@ public class PipTouchHandler {
}
/**
- * Sets the controller to update the system of changes from user interaction.
- */
- void setPinnedStackController(IPinnedStackController controller) {
- mPinnedStackController = controller;
- }
-
- /**
* Sets the menu visibility.
*/
private void setMenuState(int menuState, boolean resize, Runnable callback) {
@@ -620,13 +615,9 @@ public class PipTouchHandler {
// bounds which are now stale. In such a case we defer the animation to the
// normal bounds until after the next onMovementBoundsChanged() call to get the
// bounds in the new orientation
- try {
- int displayRotation = mPinnedStackController.getDisplayRotation();
- if (mDisplayRotation != displayRotation) {
- mDeferResizeToNormalBoundsUntilRotation = displayRotation;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Could not get display rotation from controller");
+ int displayRotation = mContext.getDisplay().getRotation();
+ if (mDisplayRotation != displayRotation) {
+ mDeferResizeToNormalBoundsUntilRotation = displayRotation;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
index 217150770084..5f2327ce98d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone;
import android.graphics.PointF;
import android.os.Handler;
import android.util.Log;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
@@ -64,6 +65,7 @@ public class PipTouchState {
private boolean mStartedDragging = false;
private boolean mAllowDraggingOffscreen = false;
private int mActivePointerId;
+ private int mLastTouchDisplayId = Display.INVALID_DISPLAY;
public PipTouchState(ViewConfiguration viewConfig, Handler handler,
Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) {
@@ -81,12 +83,14 @@ public class PipTouchState {
mIsDragging = false;
mStartedDragging = false;
mIsUserInteracting = false;
+ mLastTouchDisplayId = Display.INVALID_DISPLAY;
}
/**
* Processes a given touch event and updates the state.
*/
public void onTouchEvent(MotionEvent ev) {
+ mLastTouchDisplayId = ev.getDisplayId();
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
if (!mAllowTouches) {
@@ -266,6 +270,13 @@ public class PipTouchState {
}
/**
+ * @return Display ID of the last touch event.
+ */
+ public int getLastTouchDisplayId() {
+ return mLastTouchDisplayId;
+ }
+
+ /**
* Sets whether touching is currently allowed.
*/
public void setAllowTouches(boolean allowTouches) {
@@ -378,6 +389,7 @@ public class PipTouchState {
pw.println(prefix + TAG);
pw.println(innerPrefix + "mAllowTouches=" + mAllowTouches);
pw.println(innerPrefix + "mActivePointerId=" + mActivePointerId);
+ pw.println(innerPrefix + "mLastTouchDisplayId=" + mLastTouchDisplayId);
pw.println(innerPrefix + "mDownTouch=" + mDownTouch);
pw.println(innerPrefix + "mDownDelta=" + mDownDelta);
pw.println(innerPrefix + "mLastTouch=" + mLastTouch);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 56e97b91c9d2..763370bec1c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -36,7 +36,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
@@ -59,13 +58,14 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Manages the picture-in-picture (PIP) UI and states.
*/
public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback {
- private static final String TAG = "PipController";
- static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String TAG = "TvPipController";
+ static final boolean DEBUG = false;
/**
* Unknown or invalid state
@@ -111,15 +111,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PipMediaController mPipMediaController;
+ private final TvPipMenuController mTvPipMenuController;
private IActivityTaskManager mActivityTaskManager;
private int mState = STATE_NO_PIP;
private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP;
private final Handler mHandler = new Handler();
private List<Listener> mListeners = new ArrayList<>();
- private Rect mPipBounds;
- private Rect mDefaultPipBounds = new Rect();
- private Rect mMenuModePipBounds;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private int mPipTaskId = TASK_ID_NO_PIP;
private int mPinnedStackId = INVALID_STACK_ID;
@@ -181,46 +179,33 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- mHandler.post(() -> {
- mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
- if (mState == STATE_PIP) {
- if (mImeVisible != imeVisible) {
- if (imeVisible) {
- // Save the IME height adjustment, and offset to not occlude the IME
- mPipBounds.offset(0, -imeHeight);
- mImeHeightAdjustment = imeHeight;
- } else {
- // Apply the inverse adjustment when the IME is hidden
- mPipBounds.offset(0, mImeHeightAdjustment);
- }
- mImeVisible = imeVisible;
- resizePinnedStack(STATE_PIP);
+ mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
+ if (mState == STATE_PIP) {
+ if (mImeVisible != imeVisible) {
+ if (imeVisible) {
+ // Save the IME height adjustment, and offset to not occlude the IME
+ mPipBoundsState.getNormalBounds().offset(0, -imeHeight);
+ mImeHeightAdjustment = imeHeight;
+ } else {
+ // Apply the inverse adjustment when the IME is hidden
+ mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment);
}
+ mImeVisible = imeVisible;
+ resizePinnedStack(STATE_PIP);
}
- });
+ }
}
@Override
public void onMovementBoundsChanged(boolean fromImeAdjustment) {
- mHandler.post(() -> {
- mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
-
- mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
- mPipBounds.set(mPipBoundsAlgorithm.getNormalBounds());
- if (mDefaultPipBounds.isEmpty()) {
- mDefaultPipBounds.set(mPipBoundsAlgorithm.getDefaultBounds());
- }
- });
+ mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
+ mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
}
@Override
public void onActionsChanged(ParceledListSlice<RemoteAction> actions) {
mCustomActions = actions;
- mHandler.post(() -> {
- for (int i = mListeners.size() - 1; i >= 0; --i) {
- mListeners.get(i).onPipMenuActionsChanged(mCustomActions);
- }
- });
+ mTvPipMenuController.setAppActions(mCustomActions);
}
}
@@ -228,6 +213,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
+ TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
PipNotification pipNotification,
TaskStackListenerImpl taskStackListener,
@@ -237,6 +223,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mPipNotification = pipNotification;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipMediaController = pipMediaController;
+ mTvPipMenuController = tvPipMenuController;
+ mTvPipMenuController.attachPipController(this);
// Ensure that we have the display info in case we get calls to update the bounds
// before the listener calls back
final DisplayInfo displayInfo = new DisplayInfo();
@@ -289,9 +277,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PipController.this.onActivityRestartAttempt(task, clearedTask);
}
});
-
- // TODO(b/169395392) Refactor PipMenuActivity to PipMenuView
- PipMenuActivity.setPipController(this);
}
private void loadConfigurationsAndApply(Configuration newConfig) {
@@ -302,14 +287,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
return;
}
- Resources res = mContext.getResources();
- mMenuModePipBounds = Rect.unflattenFromString(res.getString(
- R.string.pip_menu_bounds));
+ final Rect menuBounds = Rect.unflattenFromString(
+ mContext.getResources().getString(R.string.pip_menu_bounds));
+ mPipBoundsState.setExpandedBounds(menuBounds);
- // Reset the PIP bounds and apply. PIP bounds can be changed by two reasons.
- // 1. Configuration changed due to the language change (RTL <-> RTL)
- // 2. SystemUI restarts after the crash
- mPipBounds = mDefaultPipBounds;
resizePinnedStack(getPinnedTaskInfo() == null ? STATE_NO_PIP : STATE_PIP);
}
@@ -379,16 +360,22 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
private void onActivityPinned(String packageName) {
- if (DEBUG) Log.d(TAG, "onActivityPinned()");
-
- RootTaskInfo taskInfo = getPinnedTaskInfo();
+ final RootTaskInfo taskInfo = getPinnedTaskInfo();
+ if (DEBUG) Log.d(TAG, "onActivityPinned, task=" + taskInfo);
if (taskInfo == null) {
Log.w(TAG, "Cannot find pinned stack");
return;
}
- if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo);
+
+ // At this point PipBoundsState knows the correct aspect ratio for this pinned task, so we
+ // use PipBoundsAlgorithm to calculate the normal bounds for the task (PipBoundsAlgorithm
+ // will query PipBoundsState for the aspect ratio) and pass the bounds over to the
+ // PipBoundsState.
+ mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds());
+
mPinnedStackId = taskInfo.taskId;
mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1];
+
// Set state to STATE_PIP so we show it when the pinned stack animation ends.
mState = STATE_PIP;
mPipMediaController.onActivityPinned();
@@ -434,8 +421,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
}
if (getState() == STATE_PIP) {
- if (mPipBounds != mDefaultPipBounds) {
- mPipBounds = mDefaultPipBounds;
+ if (!Objects.equals(mPipBoundsState.getBounds(), mPipBoundsState.getNormalBounds())) {
resizePinnedStack(STATE_PIP);
}
}
@@ -451,7 +437,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
Log.d(TAG,
"suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
}
-
mSuspendPipResizingReason |= reason;
}
@@ -478,6 +463,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
* @param state In Pip state also used to determine the new size for the Pip.
*/
public void resizePinnedStack(int state) {
+
if (DEBUG) {
Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state="
+ getStateDescription(), new Exception());
@@ -509,11 +495,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
break;
case STATE_PIP_MENU:
- newBounds = mMenuModePipBounds;
+ newBounds = mPipBoundsState.getExpandedBounds();
break;
case STATE_PIP: // fallthrough
default:
- newBounds = mPipBounds;
+ newBounds = mPipBoundsState.getNormalBounds();
break;
}
if (newBounds != null) {
@@ -544,10 +530,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onShowPipMenu();
}
- Intent intent = new Intent(mContext, PipMenuActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(PipMenuActivity.EXTRA_CUSTOM_ACTIONS, mCustomActions);
- mContext.startActivity(intent);
+
+ mTvPipMenuController.showMenu();
}
/**
@@ -650,8 +634,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
void onPipActivityClosed();
/** Invoked when the PIP menu gets shown. */
void onShowPipMenu();
- /** Invoked when the PIP menu actions change. */
- void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions);
/** Invoked when the PIPed activity is about to return back to the fullscreen. */
void onMoveToFullscreen();
/** Invoked when we are above to start resizing the Pip. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java
index d2270c278161..689c3ede9efa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java
@@ -18,109 +18,110 @@ package com.android.wm.shell.pip.tv;
import android.animation.Animator;
import android.animation.AnimatorInflater;
-import android.app.Activity;
+import android.annotation.Nullable;
import android.app.RemoteAction;
-import android.content.Intent;
+import android.content.Context;
import android.content.pm.ParceledListSlice;
-import android.os.Bundle;
import android.util.Log;
+import android.view.KeyEvent;
+import android.view.SurfaceControl;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
+import android.widget.FrameLayout;
import com.android.wm.shell.R;
import java.util.Collections;
/**
- * Activity to show the PIP menu to control PIP.
- * TODO(b/169395392) Refactor PipMenuActivity to PipMenuView
+ * The Menu View that shows controls of the PiP. Always fullscreen.
*/
-public class PipMenuActivity extends Activity implements PipController.Listener {
- private static final String TAG = "PipMenuActivity";
+public class PipMenuView extends FrameLayout implements PipController.Listener {
+ private static final String TAG = "PipMenuView";
private static final boolean DEBUG = PipController.DEBUG;
- static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
-
- private static PipController sPipController;
-
- private Animator mFadeInAnimation;
- private Animator mFadeOutAnimation;
+ private final PipController mPipController;
+ private final Animator mFadeInAnimation;
+ private final Animator mFadeOutAnimation;
+ private final PipControlsViewController mPipControlsViewController;
private boolean mRestorePipSizeWhenClose;
- private PipControlsViewController mPipControlsViewController;
- @Override
- protected void onCreate(Bundle bundle) {
- if (DEBUG) Log.d(TAG, "onCreate()");
+ public PipMenuView(Context context, PipController pipController) {
+ super(context, null, 0);
+ mPipController = pipController;
+
+ inflate(context, R.layout.tv_pip_menu, this);
- super.onCreate(bundle);
- if (sPipController == null) {
- finish();
- }
- setContentView(R.layout.tv_pip_menu);
mPipControlsViewController = new PipControlsViewController(
- findViewById(R.id.pip_controls), sPipController);
- sPipController.addListener(this);
+ findViewById(R.id.pip_controls), mPipController);
mRestorePipSizeWhenClose = true;
mFadeInAnimation = AnimatorInflater.loadAnimator(
- this, R.anim.tv_pip_menu_fade_in_animation);
+ mContext, R.anim.tv_pip_menu_fade_in_animation);
mFadeInAnimation.setTarget(mPipControlsViewController.getView());
mFadeOutAnimation = AnimatorInflater.loadAnimator(
- this, R.anim.tv_pip_menu_fade_out_animation);
+ mContext, R.anim.tv_pip_menu_fade_out_animation);
mFadeOutAnimation.setTarget(mPipControlsViewController.getView());
-
- onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
}
@Override
- protected void onNewIntent(Intent intent) {
- if (DEBUG) Log.d(TAG, "onNewIntent(), intent=" + intent);
- super.onNewIntent(intent);
-
- onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
+ && event.getAction() == KeyEvent.ACTION_UP) {
+ restorePipAndFinish();
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
}
- private void restorePipAndFinish() {
- if (DEBUG) Log.d(TAG, "restorePipAndFinish()");
-
- if (mRestorePipSizeWhenClose) {
- if (DEBUG) Log.d(TAG, " > restoring to the default position");
-
- // When PIP menu activity is closed, restore to the default position.
- sPipController.resizePinnedStack(PipController.STATE_PIP);
+ @Nullable
+ SurfaceControl getWindowSurfaceControl() {
+ final ViewRootImpl root = getViewRootImpl();
+ if (root == null) {
+ return null;
+ }
+ final SurfaceControl out = root.getSurfaceControl();
+ if (out != null && out.isValid()) {
+ return out;
}
- finish();
+ return null;
}
- @Override
- public void onResume() {
- if (DEBUG) Log.d(TAG, "onResume()");
-
- super.onResume();
+ void showMenu() {
+ mPipController.addListener(this);
mFadeInAnimation.start();
+ setAlpha(1.0f);
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ getViewRootImpl().getInputToken(), true /* grantFocus */);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to update focus as menu appears", e);
+ }
}
- @Override
- public void onPause() {
- if (DEBUG) Log.d(TAG, "onPause()");
-
- super.onPause();
+ void hideMenu() {
+ mPipController.removeListener(this);
+ mPipController.resumePipResizing(
+ PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
mFadeOutAnimation.start();
- restorePipAndFinish();
+ setAlpha(0.0f);
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ getViewRootImpl().getInputToken(), false /* grantFocus */);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to update focus as menu disappears", e);
+ }
}
- @Override
- protected void onDestroy() {
- if (DEBUG) Log.d(TAG, "onDestroy()");
-
- super.onDestroy();
- sPipController.removeListener(this);
- sPipController.resumePipResizing(
- PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
- }
+ private void restorePipAndFinish() {
+ if (DEBUG) Log.d(TAG, "restorePipAndFinish()");
- @Override
- public void onBackPressed() {
- if (DEBUG) Log.d(TAG, "onBackPressed()");
+ if (mRestorePipSizeWhenClose) {
+ if (DEBUG) Log.d(TAG, " > restoring to the default position");
- restorePipAndFinish();
+ // When PIP menu activity is closed, restore to the default position.
+ mPipController.resizePinnedStack(PipController.STATE_PIP);
+ }
+ hideMenu();
}
@Override
@@ -132,11 +133,10 @@ public class PipMenuActivity extends Activity implements PipController.Listener
public void onPipActivityClosed() {
if (DEBUG) Log.d(TAG, "onPipActivityClosed()");
- finish();
+ hideMenu();
}
- @Override
- public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) {
+ void setAppActions(ParceledListSlice<RemoteAction> actions) {
if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()");
boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
@@ -156,34 +156,15 @@ public class PipMenuActivity extends Activity implements PipController.Listener
// Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds.
// This conflicts with restoring PIP position, so disable it.
mRestorePipSizeWhenClose = false;
- finish();
+ hideMenu();
}
@Override
public void onPipResizeAboutToStart() {
if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()");
- finish();
- sPipController.suspendPipResizing(
+ hideMenu();
+ mPipController.suspendPipResizing(
PipController.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
}
-
- @Override
- public void finish() {
- if (DEBUG) Log.d(TAG, "finish()", new RuntimeException());
-
- super.finish();
- }
-
- /**
- * TODO(b/169395392) Refactor PipMenuActivity to PipMenuView
- *
- * @param pipController The singleton pipController instance for TV
- */
- public static void setPipController(PipController pipController) {
- if (sPipController != null) {
- return;
- }
- sPipController = pipController;
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
index b30dee4f331f..d56a88874420 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
@@ -19,12 +19,10 @@ package com.android.wm.shell.pip.tv;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.RemoteAction;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
@@ -99,11 +97,6 @@ public class PipNotification implements PipController.Listener {
}
@Override
- public void onPipMenuActionsChanged(ParceledListSlice<RemoteAction> actions) {
- // no-op.
- }
-
- @Override
public void onMoveToFullscreen() {
dismissPipNotification();
mPackageName = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
new file mode 100644
index 000000000000..91aef670b946
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.pip.tv;
+
+import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
+
+import android.app.RemoteAction;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMenuController;
+
+/**
+ * Manages the visibility of the PiP Menu as user interacts with PiP.
+ */
+public class TvPipMenuController implements PipMenuController {
+
+ private final Context mContext;
+ private final SystemWindows mSystemWindows;
+ private final PipBoundsState mPipBoundsState;
+ private PipMenuView mMenuView;
+ private PipController mPipController;
+ private SurfaceControl mLeash;
+
+ public TvPipMenuController(Context context, PipBoundsState pipBoundsState,
+ SystemWindows systemWindows) {
+ mContext = context;
+ mPipBoundsState = pipBoundsState;
+ mSystemWindows = systemWindows;
+ }
+
+ void attachPipController(PipController pipController) {
+ mPipController = pipController;
+ }
+
+ @Override
+ public void showMenu() {
+ if (mMenuView != null) {
+ mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE,
+ mPipBoundsState.getDisplayBounds().width(),
+ mPipBoundsState.getDisplayBounds().height()));
+ mMenuView.showMenu();
+
+ // By default, SystemWindows views are above everything else.
+ // Set the relative z-order so the menu is below PiP.
+ if (mMenuView.getWindowSurfaceControl() != null && mLeash != null) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, -1);
+ t.apply();
+ }
+ }
+ }
+
+ @Override
+ public void attach(SurfaceControl leash) {
+ if (mMenuView == null) {
+ mMenuView = new PipMenuView(mContext, mPipController);
+ mSystemWindows.addView(mMenuView,
+ getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
+ 0, SHELL_ROOT_LAYER_PIP);
+ mLeash = leash;
+ }
+ }
+
+ @Override
+ public void detach() {
+ mSystemWindows.removeView(mMenuView);
+ mMenuView = null;
+ mLeash = null;
+ }
+
+ @Override
+ public void setAppActions(ParceledListSlice<RemoteAction> appActions) {
+ mMenuView.setAppActions(appActions);
+ }
+
+ @Override
+ public boolean isMenuVisible() {
+ return mMenuView != null && mMenuView.getAlpha() == 1.0f;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index e55f065c1bb2..7c70a4efad91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -19,6 +19,8 @@ package com.android.wm.shell.splitscreen;
import android.graphics.Rect;
import android.window.WindowContainerToken;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
import java.io.PrintWriter;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -26,6 +28,7 @@ import java.util.function.Consumer;
/**
* Interface to engage split screen feature.
*/
+@ExternalThread
public interface SplitScreen {
/** Called when keyguard showing state changed. */
void onKeyguardVisibilityChanged(boolean isShowing);
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
index 101b5bf27c77..1054c4345891 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
@@ -21,6 +21,8 @@
<!-- Read and write traces from external storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<!-- Write secure settings -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<!-- Capture screen contents -->
@@ -38,7 +40,8 @@
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
<!-- ATM.removeRootTasksWithActivityTypes() -->
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
- <application>
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <application android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
<service android:name=".NotificationListener"
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
index 9dd9f42bdf81..23d7021baffb 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestPhysicalDevices.xml
@@ -34,7 +34,7 @@
<option name="hidden-api-checks" value="false" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.wm.shell.flicker/files" />
+ <option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
index afb1166415fc..073860875004 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestVirtualDevices.xml
@@ -34,7 +34,7 @@
<option name="hidden-api-checks" value="false" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.wm.shell.flicker/files" />
+ <option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 0fb43e263d05..3ed53fb221a7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,78 +18,6 @@ package com.android.wm.shell.flicker
import com.android.server.wm.flicker.dsl.EventLogAssertion
import com.android.server.wm.flicker.dsl.LayersAssertion
-import com.android.server.wm.flicker.dsl.WmAssertion
-import com.android.server.wm.flicker.helpers.WindowUtils
-
-@JvmOverloads
-fun WmAssertion.statusBarWindowIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
- }
-}
-
-@JvmOverloads
-fun WmAssertion.navBarWindowIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("navBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
- }
-}
-
-@JvmOverloads
-fun LayersAssertion.noUncoveredRegions(
- beginRotation: Int,
- endRotation: Int = beginRotation,
- allStates: Boolean = true,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
- val endingBounds = WindowUtils.getDisplayBounds(endRotation)
- if (allStates) {
- all("noUncoveredRegions", bugId, enabled) {
- if (startingBounds == endingBounds) {
- this.coversAtLeastRegion(startingBounds)
- } else {
- this.coversAtLeastRegion(startingBounds)
- .then()
- .coversAtLeastRegion(endingBounds)
- }
- }
- } else {
- start("noUncoveredRegions_StartingPos") {
- this.coversAtLeastRegion(startingBounds)
- }
- end("noUncoveredRegions_EndingPos") {
- this.coversAtLeastRegion(endingBounds)
- }
- }
-}
-
-@JvmOverloads
-fun LayersAssertion.statusBarLayerIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
- }
-}
-
-@JvmOverloads
-fun LayersAssertion.navBarLayerIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("navBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
- }
-}
@JvmOverloads
fun LayersAssertion.appPairsDividerIsVisible(
@@ -131,48 +59,6 @@ fun LayersAssertion.dockedStackDividerIsInvisible(
}
}
-@JvmOverloads
-fun LayersAssertion.navBarLayerRotatesAndScales(
- beginRotation: Int,
- endRotation: Int = beginRotation,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
- val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
-
- start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) {
- this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
- }
- end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) {
- this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos)
- }
-
- if (startingPos == endingPos) {
- all("navBarLayerRotatesAndScales", bugId, enabled) {
- this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
- }
- }
-}
-
-@JvmOverloads
-fun LayersAssertion.statusBarLayerRotatesScales(
- beginRotation: Int,
- endRotation: Int = beginRotation,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
- val endingPos = WindowUtils.getStatusBarPosition(endRotation)
-
- start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) {
- this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos)
- }
- end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) {
- this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos)
- }
-}
-
fun EventLogAssertion.focusChanges(
vararg windows: String,
bugId: Int = 0,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
index ced99de21a46..7ac91b065fca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
@@ -29,10 +29,10 @@ import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 0663eb344f46..c9396aa7ea63 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -23,10 +23,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
index 322034ce7688..76aabc1b83cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
@@ -24,10 +24,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 96d98d56e069..a67b3b760c49 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -31,13 +31,13 @@ import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.navBarLayerRotatesAndScales
-import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.noUncoveredRegions
-import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarLayerRotatesScales
-import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt
index d20552f0739d..f79b21ff278d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt
@@ -28,10 +28,10 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
index c61a0f171714..5570a562a515 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
@@ -25,10 +25,10 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.dockedStackDividerIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
-import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
-import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
index bf9286980b9a..7c47d1f1b1ae 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
@@ -29,8 +29,8 @@ import com.android.server.wm.flicker.helpers.resizeSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
-import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java
deleted file mode 100644
index c9d32c4b1f76..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2020 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.wm.shell.apppairs;
-
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.view.Display;
-import android.view.SurfaceControl;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTestCase;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/** Tests for {@link AppPairLayout} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AppPairLayoutTests extends ShellTestCase {
- @Mock SurfaceControl mSurfaceControl;
- private Display mDisplay;
- private Configuration mConfiguration;
- private AppPairLayout mAppPairLayout;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mConfiguration = getConfiguration(false);
- mDisplay = mContext.getDisplay();
- mAppPairLayout = new AppPairLayout(mContext, mDisplay, mConfiguration, mSurfaceControl);
- }
-
- @After
- @UiThreadTest
- public void tearDown() {
- mAppPairLayout.release();
- }
-
- @Test
- @UiThreadTest
- public void testUpdateConfiguration() {
- assertThat(mAppPairLayout.updateConfiguration(getConfiguration(false))).isFalse();
- assertThat(mAppPairLayout.updateConfiguration(getConfiguration(true))).isTrue();
- }
-
- @Test
- @UiThreadTest
- public void testInitRelease() {
- mAppPairLayout.init();
- assertThat(mAppPairLayout.getDividerLeash()).isNotNull();
- mAppPairLayout.release();
- assertThat(mAppPairLayout.getDividerLeash()).isNull();
- }
-
- private static Configuration getConfiguration(boolean isLandscape) {
- final Configuration configuration = new Configuration();
- configuration.unset();
- configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
- configuration.windowConfiguration.setBounds(
- new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160));
- return configuration;
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
index f12648a7f709..8dbc1d56bcc2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
@@ -55,13 +55,13 @@ public class AppPairTests extends ShellTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
+ when(mDisplayController.getDisplay(anyInt())).thenReturn(
+ mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
mController = new TestAppPairsController(
mTaskOrganizer,
mSyncQueue,
mDisplayController);
- when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
- when(mDisplayController.getDisplay(anyInt())).thenReturn(
- mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
}
@After
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
index f8c68d2018da..fada694a4c07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
@@ -55,14 +55,14 @@ public class AppPairsControllerTests extends ShellTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
+ when(mDisplayController.getDisplay(anyInt())).thenReturn(
+ mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
mController = new TestAppPairsController(
mTaskOrganizer,
mSyncQueue,
mDisplayController);
mPool = mController.getPool();
- when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
- when(mDisplayController.getDisplay(anyInt())).thenReturn(
- mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
}
@After
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
index 8ece913de53f..a3f134ee97ed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
@@ -18,10 +18,14 @@ package com.android.wm.shell.apppairs;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -35,7 +39,7 @@ import org.mockito.MockitoAnnotations;
/** Tests for {@link AppPairsPool} */
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class AppPairsPoolTests {
+public class AppPairsPoolTests extends ShellTestCase {
private TestAppPairsController mController;
private TestAppPairsPool mPool;
@Mock private SyncTransactionQueue mSyncQueue;
@@ -45,6 +49,7 @@ public class AppPairsPoolTests {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
mController = new TestAppPairsController(
mTaskOrganizer,
mSyncQueue,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 080cddc58a09..5e0d51809d44 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -20,12 +20,15 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.Surface.ROTATION_0;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.graphics.Point;
+import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
@@ -68,19 +71,31 @@ public class DisplayImeControllerTest {
@Test
public void reappliesVisibilityToChangedLeash() {
verifyZeroInteractions(mT);
+ mPerDisplay.mImeShowing = true;
- mPerDisplay.mImeShowing = false;
- mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] {
- new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0))
- });
+ mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
+ assertFalse(mPerDisplay.mImeShowing);
verify(mT).hide(any());
mPerDisplay.mImeShowing = true;
- mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] {
- new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0))
- });
+ mPerDisplay.insetsControlChanged(insetsStateWithIme(true), insetsSourceControl());
+ assertTrue(mPerDisplay.mImeShowing);
verify(mT).show(any());
}
+
+ private InsetsSourceControl[] insetsSourceControl() {
+ return new InsetsSourceControl[]{
+ new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0))
+ };
+ }
+
+ private InsetsState insetsStateWithIme(boolean visible) {
+ InsetsState state = new InsetsState();
+ state.addSource(new InsetsSource(ITYPE_IME));
+ state.setSourceVisible(ITYPE_IME, visible);
+ return state;
+ }
+
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
new file mode 100644
index 000000000000..d87f4c60fad4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.common.split;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link SplitLayout} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SplitLayoutTests extends ShellTestCase {
+ @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener;
+ @Mock SurfaceControl mRootLeash;
+ private SplitLayout mSplitLayout;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mSplitLayout = new SplitLayout(
+ mContext,
+ getConfiguration(false),
+ mLayoutChangeListener,
+ mRootLeash);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testUpdateConfiguration() {
+ assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse();
+ assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue();
+ }
+
+ @Test
+ public void testUpdateDividePosition() {
+ mSplitLayout.updateDividePosition(anyInt());
+ verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class));
+ }
+
+ @Test
+ public void testSetSnapTarget() {
+ DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0,
+ DividerSnapAlgorithm.SnapTarget.FLAG_NONE);
+ mSplitLayout.setSnapTarget(snapTarget);
+ verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class));
+
+ // verify it callbacks properly when the snap target indicates dismissing split.
+ snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
+ mSplitLayout.setSnapTarget(snapTarget);
+ verify(mLayoutChangeListener).onSnappedToDismiss(eq(false));
+ snapTarget = getSnapTarget(0, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
+ mSplitLayout.setSnapTarget(snapTarget);
+ verify(mLayoutChangeListener).onSnappedToDismiss(eq(true));
+ }
+
+ private static Configuration getConfiguration(boolean isLandscape) {
+ final Configuration configuration = new Configuration();
+ configuration.unset();
+ configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+ configuration.windowConfiguration.setBounds(
+ new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160));
+ return configuration;
+ }
+
+ private static DividerSnapAlgorithm.SnapTarget getSnapTarget(int position, int flag) {
+ return new DividerSnapAlgorithm.SnapTarget(
+ position /* position */, position /* taskPosition */, flag);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
new file mode 100644
index 000000000000..aa0eb2f95ed8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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.wm.shell.common.split;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link SplitWindowManager} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SplitWindowManagerTests extends ShellTestCase {
+ @Mock SurfaceControl mSurfaceControl;
+ @Mock SplitLayout mSplitLayout;
+ private SplitWindowManager mSplitWindowManager;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ final Configuration configuration = new Configuration();
+ configuration.setToDefaults();
+ mSplitWindowManager = new SplitWindowManager(mContext, configuration, mSurfaceControl);
+ when(mSplitLayout.getDividerBounds()).thenReturn(
+ new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
+ configuration.windowConfiguration.getBounds().height()));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testInitRelease() {
+ mSplitWindowManager.init(mSplitLayout);
+ assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull();
+ mSplitWindowManager.release();
+ assertThat(mSplitWindowManager.getSurfaceControl()).isNull();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 55e7a354f4cd..7f280cd124d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -79,7 +79,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), new Rect(), null, TRANSITION_DIRECTION_TO_PIP);
+ .getAnimator(mLeash, new Rect(), new Rect(), new Rect(), null,
+ TRANSITION_DIRECTION_TO_PIP);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -87,16 +88,19 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
public void getAnimator_whenSameTypeRunning_updateExistingAnimator() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
+ .getAnimator(mLeash, baseValue, startValue, endValue1, null,
+ TRANSITION_DIRECTION_TO_PIP);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue2, null, TRANSITION_DIRECTION_TO_PIP);
+ .getAnimator(mLeash, baseValue, startValue, endValue2, null,
+ TRANSITION_DIRECTION_TO_PIP);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -122,11 +126,13 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
@SuppressWarnings("unchecked")
public void pipTransitionAnimator_updateEndValue() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
+ .getAnimator(mLeash, baseValue, startValue, endValue1, null,
+ TRANSITION_DIRECTION_TO_PIP);
animator.updateEndValue(endValue2);
@@ -135,10 +141,12 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
public void pipTransitionAnimator_setPipAnimationCallback() {
+ final Rect baseValue = new Rect(0, 0, 100, 100);
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue, null, TRANSITION_DIRECTION_TO_PIP);
+ .getAnimator(mLeash, baseValue, startValue, endValue, null,
+ TRANSITION_DIRECTION_TO_PIP);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 8d3774cee1e0..45e4241d5bc6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -44,7 +44,7 @@ import android.window.WindowContainerToken;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.pip.phone.PipMenuActivityController;
+import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
@@ -66,7 +66,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private DisplayController mMockdDisplayController;
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
- @Mock private PipMenuActivityController mMenuActivityController;
+ @Mock private PhonePipMenuController mMockPhonePipMenuController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
@@ -83,9 +83,9 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext);
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
- mMockPipBoundsAlgorithm, mMenuActivityController, mMockPipSurfaceTransactionHelper,
- mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger,
- mMockShellTaskOrganizer));
+ mMockPipBoundsAlgorithm, mMockPhonePipMenuController,
+ mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
+ mMockPipUiEventLogger, mMockShellTaskOrganizer));
preparePipTaskOrg();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 88c8eb902a6f..4687d2d9667c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -61,7 +61,7 @@ public class PipControllerTest extends ShellTestCase {
private PipController mPipController;
@Mock private DisplayController mMockDisplayController;
- @Mock private PipMenuActivityController mMockPipMenuActivityController;
+ @Mock private PhonePipMenuController mMockPhonePipMenuController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
@Mock private PipMediaController mMockPipMediaController;
@@ -77,7 +77,7 @@ public class PipControllerTest extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mPipController = new PipController(mContext, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
- mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
+ mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
mMockExecutor);
doAnswer(invocation -> {
@@ -110,7 +110,7 @@ public class PipControllerTest extends ShellTestCase {
assertNull(PipController.create(spyContext, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
- mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
+ mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
mMockExecutor));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index abbc681f53fe..4efaebf96c2b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -60,7 +61,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
private PipTouchHandler mPipTouchHandler;
@Mock
- private PipMenuActivityController mPipMenuActivityController;
+ private PhonePipMenuController mPhonePipMenuController;
@Mock
private PipTaskOrganizer mPipTaskOrganizer;
@@ -71,6 +72,9 @@ public class PipTouchHandlerTest extends ShellTestCase {
@Mock
private PipUiEventLogger mPipUiEventLogger;
+ @Mock
+ private ShellExecutor mShellMainExecutor;
+
private PipBoundsState mPipBoundsState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipSnapAlgorithm mPipSnapAlgorithm;
@@ -92,9 +96,9 @@ public class PipTouchHandlerTest extends ShellTestCase {
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
mPipSnapAlgorithm = new PipSnapAlgorithm();
- mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
+ mPipTouchHandler = new PipTouchHandler(mContext, mPhonePipMenuController,
mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
- mFloatingContentCoordinator, mPipUiEventLogger);
+ mFloatingContentCoordinator, mPipUiEventLogger, mShellMainExecutor);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index cd53217d2924..1ff1978044b9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -333,6 +333,7 @@ cc_defaults {
"jni/YuvToJpegEncoder.cpp",
"jni/fonts/Font.cpp",
"jni/fonts/FontFamily.cpp",
+ "jni/fonts/NativeFont.cpp",
"jni/text/LineBreaker.cpp",
"jni/text/MeasuredText.cpp",
"jni/text/TextShaper.cpp",
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index fd18d2f9192d..8b20492543f7 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -20,7 +20,7 @@
namespace android {
namespace uirenderer {
-const std::string FrameInfoNames[] = {
+const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = {
"Flags",
"FrameTimelineVsyncId",
"IntendedVsync",
@@ -42,10 +42,6 @@ const std::string FrameInfoNames[] = {
"GpuCompleted",
};
-static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) ==
- static_cast<int>(FrameInfoIndex::NumIndexes),
- "size mismatch: FrameInfoNames doesn't match the enum!");
-
static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19,
"Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index bb875e35f6f7..738246d56d0d 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -21,6 +21,7 @@
#include <cutils/compiler.h>
#include <utils/Timers.h>
+#include <array>
#include <memory.h>
#include <string>
@@ -60,7 +61,7 @@ enum class FrameInfoIndex {
NumIndexes
};
-extern const std::string FrameInfoNames[];
+extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames;
namespace FrameInfoFlags {
enum {
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index c174c240ff22..f4c633fbe58f 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,6 +18,7 @@
#include "renderstate/RenderState.h"
#include "utils/Color.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -52,5 +53,90 @@ SkBlendMode Layer::getMode() const {
}
}
+static inline SkScalar isIntegerAligned(SkScalar x) {
+ return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON;
+}
+
+// Disable filtering when there is no scaling in screen coordinates and the corners have the same
+// fraction (for translate) or zero fraction (for any other rect-to-rect transform).
+static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) {
+ if (!matrix.rectStaysRect()) return true;
+ SkRect dstDevRect = matrix.mapRect(dstRect);
+ float dstW, dstH;
+ if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) {
+ // Has a 90 or 270 degree rotation, although total matrix may also have scale factors
+ // in m10 and m01. Those scalings are automatically handled by mapRect so comparing
+ // dimensions is sufficient, but swap width and height comparison.
+ dstW = dstDevRect.height();
+ dstH = dstDevRect.width();
+ } else {
+ // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but
+ // dimensions are still safe to compare directly.
+ dstW = dstDevRect.width();
+ dstH = dstDevRect.height();
+ }
+ if (!(MathUtils::areEqual(dstW, srcRect.width()) &&
+ MathUtils::areEqual(dstH, srcRect.height()))) {
+ return true;
+ }
+ // Device rect and source rect should be integer aligned to ensure there's no difference
+ // in how nearest-neighbor sampling is resolved.
+ return !(isIntegerAligned(srcRect.x()) &&
+ isIntegerAligned(srcRect.y()) &&
+ isIntegerAligned(dstDevRect.x()) &&
+ isIntegerAligned(dstDevRect.y()));
+}
+
+void Layer::draw(SkCanvas* canvas) {
+ GrRecordingContext* context = canvas->recordingContext();
+ if (context == nullptr) {
+ SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+ return;
+ }
+ SkMatrix layerTransform = getTransform();
+ //sk_sp<SkImage> layerImage = getImage();
+ const int layerWidth = getWidth();
+ const int layerHeight = getHeight();
+ if (layerImage) {
+ SkMatrix textureMatrixInv;
+ textureMatrixInv = getTexTransform();
+ // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
+ // use bottom left origin and remove flipV and invert transformations.
+ SkMatrix flipV;
+ flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
+ textureMatrixInv.preConcat(flipV);
+ textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight);
+ textureMatrixInv.postScale(layerImage->width(), layerImage->height());
+ SkMatrix textureMatrix;
+ if (!textureMatrixInv.invert(&textureMatrix)) {
+ textureMatrix = textureMatrixInv;
+ }
+
+ SkMatrix matrix;
+ matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+
+ SkPaint paint;
+ paint.setAlpha(getAlpha());
+ paint.setBlendMode(getMode());
+ paint.setColorFilter(getColorFilter());
+ const bool nonIdentityMatrix = !matrix.isIdentity();
+ if (nonIdentityMatrix) {
+ canvas->save();
+ canvas->concat(matrix);
+ }
+ const SkMatrix& totalMatrix = canvas->getTotalMatrix();
+
+ SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height());
+ if (getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) {
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ }
+ canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ // restore the original matrix
+ if (nonIdentityMatrix) {
+ canvas->restore();
+ }
+ }
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index ea3bfc9e80cb..e99e76299317 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -21,6 +21,7 @@
#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkColorSpace.h>
+#include <SkCanvas.h>
#include <SkPaint.h>
#include <SkImage.h>
#include <SkMatrix.h>
@@ -87,6 +88,8 @@ public:
inline sk_sp<SkImage> getImage() const { return this->layerImage; }
+ void draw(SkCanvas* canvas);
+
protected:
RenderState& mRenderState;
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index e1f5abd786bf..0fad2d58cc8a 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -69,6 +69,7 @@ extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_fonts_Font(JNIEnv* env);
extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
+extern int register_android_graphics_fonts_NativeFont(JNIEnv* env);
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -135,6 +136,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_drawable_VectorDrawable),
REG_JNI(register_android_graphics_fonts_Font),
REG_JNI(register_android_graphics_fonts_FontFamily),
+ REG_JNI(register_android_graphics_fonts_NativeFont),
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_graphics_pdf_PdfEditor),
REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp
index 25129f641c00..0093c38cf8a8 100644
--- a/libs/hwui/canvas/CanvasOpRasterizer.cpp
+++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp
@@ -33,7 +33,11 @@ void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination)
SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I());
source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) {
- if constexpr (T == CanvasOpType::BeginZ || T == CanvasOpType::EndZ) {
+ if constexpr (
+ T == CanvasOpType::BeginZ ||
+ T == CanvasOpType::EndZ ||
+ T == CanvasOpType::DrawLayer
+ ) {
// Do beginZ or endZ
LOG_ALWAYS_FATAL("TODO");
return;
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
index f9df2f7aa5ba..f0aa7774a6cc 100644
--- a/libs/hwui/canvas/CanvasOpTypes.h
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -47,14 +47,17 @@ enum class CanvasOpType : int8_t {
DrawArc,
DrawPaint,
DrawPoint,
+ DrawPoints,
DrawPath,
DrawLine,
+ DrawLines,
DrawVertices,
DrawImage,
DrawImageRect,
// DrawImageLattice also used to draw 9 patches
DrawImageLattice,
DrawPicture,
+ DrawLayer,
// TODO: Rest
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 8c7113d5d075..242dbdb27362 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -26,8 +26,10 @@
#include <hwui/Bitmap.h>
#include <log/log.h>
#include "CanvasProperty.h"
+#include "Points.h"
#include "CanvasOpTypes.h"
+#include "Layer.h"
#include <experimental/type_traits>
#include <utility>
@@ -165,6 +167,22 @@ struct CanvasOp<CanvasOpType::DrawPoint> {
};
template <>
+struct CanvasOp<CanvasOpType::DrawPoints> {
+ size_t count;
+ SkPaint paint;
+ sk_sp<Points> points;
+ void draw(SkCanvas* canvas) const {
+ canvas->drawPoints(
+ SkCanvas::kPoints_PointMode,
+ count,
+ points->data(),
+ paint
+ );
+ }
+ ASSERT_DRAWABLE()
+};
+
+template <>
struct CanvasOp<CanvasOpType::DrawRect> {
SkRect rect;
SkPaint paint;
@@ -263,6 +281,22 @@ struct CanvasOp<CanvasOpType::DrawLine> {
};
template<>
+struct CanvasOp<CanvasOpType::DrawLines> {
+ size_t count;
+ SkPaint paint;
+ sk_sp<Points> points;
+ void draw(SkCanvas* canvas) const {
+ canvas->drawPoints(
+ SkCanvas::kLines_PointMode,
+ count,
+ points->data(),
+ paint
+ );
+ }
+ ASSERT_DRAWABLE()
+};
+
+template<>
struct CanvasOp<CanvasOpType::DrawVertices> {
sk_sp<SkVertices> vertices;
SkBlendMode mode;
@@ -364,6 +398,11 @@ struct CanvasOp<CanvasOpType::DrawPicture> {
}
};
+template<>
+struct CanvasOp<CanvasOpType::DrawLayer> {
+ sp<Layer> layer;
+};
+
// cleanup our macros
#undef ASSERT_DRAWABLE
diff --git a/libs/hwui/canvas/Points.h b/libs/hwui/canvas/Points.h
new file mode 100644
index 000000000000..05e6a7dd5884
--- /dev/null
+++ b/libs/hwui/canvas/Points.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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 <ui/FatVector.h>
+#include "SkPoint.h"
+#include "SkRefCnt.h"
+
+/**
+ * Collection of points that are ref counted and to be used with
+ * various drawing calls that consume SkPoint as inputs like
+ * drawLines/drawPoints
+ */
+class Points: public SkNVRefCnt<SkPoint> {
+public:
+ Points(int size){
+ skPoints.resize(size);
+ }
+
+ Points(std::initializer_list<SkPoint> init): skPoints(init) { }
+
+ SkPoint& operator[](int index) {
+ return skPoints[index];
+ }
+
+ const SkPoint* data() const {
+ return skPoints.data();
+ }
+
+ size_t size() const {
+ return skPoints.size();
+ }
+private:
+ // Initialize the size to contain 2 SkPoints on the stack for optimized
+ // drawLine calls that require 2 SkPoints for start/end points of the line
+ android::FatVector<SkPoint, 2> skPoints;
+};
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index f0c77930cbe3..f612bce748ff 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -187,38 +187,6 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong
}
// Critical Native
-static jlong Font_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-
- uint64_t result = font->style().weight();
- result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
- result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
- result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
- return result;
-}
-
-// Critical Native
-static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
- const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
- uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
- return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
-}
-
-// FastNative
-static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
- const std::string& filePath = minikinSkia->getFilePath();
- if (filePath.empty()) {
- return nullptr;
- }
- return env->NewStringUTF(filePath.c_str());
-}
-
-// Critical Native
static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
return reinterpret_cast<jlong>(font->font.get());
@@ -276,9 +244,6 @@ static const JNINativeMethod gFontBuilderMethods[] = {
static const JNINativeMethod gFontMethods[] = {
{ "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
{ "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
- { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo },
- { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo },
- { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath },
{ "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
{ "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress },
};
diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp
new file mode 100644
index 000000000000..c5c5d464ccac
--- /dev/null
+++ b/libs/hwui/jni/fonts/NativeFont.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "Minikin"
+
+#include "Font.h"
+#include "SkData.h"
+#include "SkFont.h"
+#include "SkFontMetrics.h"
+#include "SkFontMgr.h"
+#include "SkRefCnt.h"
+#include "SkTypeface.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedUtfChars.h>
+#include "Utils.h"
+#include "FontUtils.h"
+
+#include <hwui/MinikinSkia.h>
+#include <hwui/Paint.h>
+#include <hwui/Typeface.h>
+#include <minikin/FontFamily.h>
+#include <minikin/LocaleList.h>
+#include <ui/FatVector.h>
+
+#include <memory>
+
+namespace android {
+
+// Critical Native
+static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) {
+ Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
+ return tf->fFontCollection->getFamilies().size();
+}
+
+// Critical Native
+static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) {
+ Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
+ return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get());
+
+}
+
+// Fast Native
+static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) {
+ minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
+ uint32_t localeListId = family->localeListId();
+ return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str());
+}
+
+// Critical Native
+static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) {
+ minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
+ return family->getNumFonts();
+}
+
+// Critical Native
+static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) {
+ minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
+ return reinterpret_cast<jlong>(family->getFont(index));
+}
+
+// Critical Native
+static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
+ const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
+
+ uint64_t result = font->style().weight();
+ result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
+ result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
+ result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
+ return result;
+}
+
+// Critical Native
+static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
+ const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
+ const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
+ uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
+ return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
+}
+
+// FastNative
+static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
+ const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
+ const std::string& filePath = minikinSkia->getFilePath();
+ if (filePath.empty()) {
+ return nullptr;
+ }
+ return env->NewStringUTF(filePath.c_str());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gNativeFontMethods[] = {
+ { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount },
+ { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily },
+ { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList },
+ { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount },
+ { "nGetFont", "(JI)J", (void*) NativeFont_getFont },
+ { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo },
+ { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo },
+ { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath },
+};
+
+int register_android_graphics_fonts_NativeFont(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods,
+ NELEM(gNativeFontMethods));
+}
+
+} // namespace android
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index f186e55ec2e3..033a5872ead3 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -245,6 +245,31 @@ TEST(CanvasOp, simpleDrawPoint) {
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
+TEST(CanvasOp, simpleDrawPoints) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+ size_t numPts = 3;
+ auto pts = sk_ref_sp(
+ new Points({
+ {32, 16},
+ {48, 48},
+ {16, 32}
+ })
+ );
+
+ buffer.push(CanvasOp<Op::DrawPoints> {
+ .count = numPts,
+ .paint = SkPaint{},
+ .points = pts
+ });
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawPoints);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
TEST(CanvasOp, simpleDrawLine) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
@@ -263,6 +288,30 @@ TEST(CanvasOp, simpleDrawLine) {
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
+TEST(CanvasOp, simpleDrawLines) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+ size_t numPts = 3;
+ auto pts = sk_ref_sp(
+ new Points({
+ {32, 16},
+ {48, 48},
+ {16, 32}
+ })
+ );
+ buffer.push(CanvasOp<Op::DrawLines> {
+ .count = numPts,
+ .paint = SkPaint{},
+ .points = pts
+ });
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawPoints);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
TEST(CanvasOp, simpleDrawRect) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 047b809bc766..604c4a1de8f9 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1509,8 +1509,15 @@ public class LocationManager {
}
/**
- * Removes location updates for the specified {@link LocationListener}. Following this call,
- * the listener will not receive any more invocations of any kind.
+ * Removes all location updates for the specified {@link LocationListener}. The given listener
+ * is guaranteed not to receive any invocations that <b>happens-after</b> this method is
+ * invoked.
+ *
+ * <p>If the given listener has any batched requests, this method will not flush any incomplete
+ * location batches before stopping location updates. If you wish to flush any pending locations
+ * before stopping, you must first call {@link #requestFlush(String, LocationListener, int)} and
+ * then call this method once the flush is complete. If this method is invoked before the flush
+ * is complete, you may not receive the flushed locations.
*
* @param listener listener that no longer needs location updates
*
@@ -1537,6 +1544,8 @@ public class LocationManager {
* Removes location updates for the specified {@link PendingIntent}. Following this call, the
* PendingIntent will no longer receive location updates.
*
+ * <p>See {@link #removeUpdates(LocationListener)} for more detail on how this method works.
+ *
* @param pendingIntent pending intent that no longer needs location updates
*
* @throws IllegalArgumentException if pendingIntent is null
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 39bdf9557595..bd27f6d068bc 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -18,6 +18,7 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.bluetooth.BluetoothCodecConfig;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1550,9 +1551,11 @@ public class AudioSystem
/** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */
@TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
public static native float getMasterBalance();
/** @hide Changes the audio balance of the device. */
@TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
public static native int setMasterBalance(float balance);
// helpers for android.media.AudioManager.getProperty(), see description there for meaning
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4b11e3231aba..f8311cd580a9 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -4232,8 +4232,10 @@ public class MediaPlayer extends PlayerBase
* @see OnRtpRxNoticeListener
*
* @param listener the listener called after a notice from RTP Rx
- * @param handler the {@link Handler} that receives RTP Tx events
- *
+ * @param handler the {@link Handler} that receives RTP Tx events. If null is passed,
+ * notifications will be posted on the thread that created this MediaPlayer
+ * instance. If the creating thread does not have a {@link Looper}, then
+ * notifications will be posted on the main thread.
* @hide
*/
@SystemApi
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index e9bb7f8d6cb8..1da41fb87b40 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -46,6 +46,8 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -437,7 +439,7 @@ public final class MediaController {
}
if (mSessionInfo == null) {
- Log.w(TAG, "sessionInfo shouldn't be null.");
+ Log.d(TAG, "sessionInfo is not set.");
mSessionInfo = Bundle.EMPTY;
} else if (MediaSession.hasCustomParcelable(mSessionInfo)) {
Log.w(TAG, "sessionInfo contains custom parcelable. Ignoring.");
@@ -514,6 +516,17 @@ public final class MediaController {
return success;
}
+ /**
+ * Gets associated handler for the given callback.
+ * @hide
+ */
+ @VisibleForTesting
+ public Handler getHandlerForCallback(Callback cb) {
+ synchronized (mLock) {
+ return getHandlerForCallbackLocked(cb);
+ }
+ }
+
private MessageHandler getHandlerForCallbackLocked(Callback cb) {
if (cb == null) {
throw new IllegalArgumentException("Callback cannot be null");
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index a9da77230214..da14ee1f3212 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -60,6 +60,7 @@ import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -296,8 +297,8 @@ public class Tuner implements AutoCloseable {
private Executor mOnResourceLostListenerExecutor;
private Integer mDemuxHandle;
- private Map<Integer, Descrambler> mDescramblers = new HashMap<>();
- private List<Filter> mFilters = new ArrayList<>();
+ private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>();
+ private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>();
private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
new TunerResourceManager.ResourcesReclaimListener() {
@@ -308,6 +309,7 @@ public class Tuner implements AutoCloseable {
.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
}
+ releaseAll();
mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST));
}
};
@@ -485,18 +487,28 @@ public class Tuner implements AutoCloseable {
if (mLnb != null) {
mLnb.close();
}
- if (!mDescramblers.isEmpty()) {
- for (Map.Entry<Integer, Descrambler> d : mDescramblers.entrySet()) {
- d.getValue().close();
- mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId);
+ synchronized (mDescramblers) {
+ if (!mDescramblers.isEmpty()) {
+ for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
+ Descrambler descrambler = d.getValue().get();
+ if (descrambler != null) {
+ descrambler.close();
+ }
+ mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId);
+ }
+ mDescramblers.clear();
}
- mDescramblers.clear();
}
- if (!mFilters.isEmpty()) {
- for (Filter f : mFilters) {
- f.close();
+ synchronized (mFilters) {
+ if (!mFilters.isEmpty()) {
+ for (WeakReference<Filter> weakFilter : mFilters) {
+ Filter filter = weakFilter.get();
+ if (filter != null) {
+ filter.close();
+ }
+ }
+ mFilters.clear();
}
- mFilters.clear();
}
if (mDemuxHandle != null) {
int res = nativeCloseDemux(mDemuxHandle);
@@ -610,7 +622,6 @@ public class Tuner implements AutoCloseable {
break;
}
case MSG_RESOURCE_LOST: {
- releaseAll();
if (mOnResourceLostListener != null
&& mOnResourceLostListenerExecutor != null) {
mOnResourceLostListenerExecutor.execute(
@@ -1183,7 +1194,10 @@ public class Tuner implements AutoCloseable {
if (mHandler == null) {
mHandler = createEventHandler();
}
- mFilters.add(filter);
+ synchronized (mFilters) {
+ WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter);
+ mFilters.add(weakFilter);
+ }
}
return filter;
}
@@ -1351,7 +1365,10 @@ public class Tuner implements AutoCloseable {
int handle = descramblerHandle[0];
Descrambler descrambler = nativeOpenDescramblerByHandle(handle);
if (descrambler != null) {
- mDescramblers.put(handle, descrambler);
+ synchronized (mDescramblers) {
+ WeakReference weakDescrambler = new WeakReference<Descrambler>(descrambler);
+ mDescramblers.put(handle, weakDescrambler);
+ }
} else {
mTunerResourceManager.releaseDescrambler(handle, mClientId);
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index af1ff01a3ebe..c762da69eedf 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -28,7 +28,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * Frontend status.
+ * A Frontend Status class that contains the metrics of the active frontend.
*
* @hide
*/
@@ -461,7 +461,7 @@ public class FrontendStatus {
}
/**
- * Lock status for Demod.
+ * Gets if the demod is currently locked or not.
*/
public boolean isDemodLocked() {
if (mIsDemodLocked == null) {
@@ -470,7 +470,7 @@ public class FrontendStatus {
return mIsDemodLocked;
}
/**
- * Gets Signal to Noise Ratio in thousandths of a deciBel (0.001dB).
+ * Gets the current Signal to Noise Ratio in thousandths of a deciBel (0.001dB).
*/
public int getSnr() {
if (mSnr == null) {
@@ -479,7 +479,7 @@ public class FrontendStatus {
return mSnr;
}
/**
- * Gets Bit Error Ratio.
+ * Gets the current Bit Error Ratio.
*
* <p>The number of error bit per 1 billion bits.
*/
@@ -491,7 +491,7 @@ public class FrontendStatus {
}
/**
- * Gets Packages Error Ratio.
+ * Gets the current Packages Error Ratio.
*
* <p>The number of error package per 1 billion packages.
*/
@@ -502,7 +502,7 @@ public class FrontendStatus {
return mPer;
}
/**
- * Gets Bit Error Ratio before Forward Error Correction (FEC).
+ * Gets the current Bit Error Ratio before Forward Error Correction (FEC).
*
* <p>The number of error bit per 1 billion bits before FEC.
*/
@@ -513,7 +513,7 @@ public class FrontendStatus {
return mPerBer;
}
/**
- * Gets Signal Quality in percent.
+ * Gets the current Signal Quality in percent.
*/
public int getSignalQuality() {
if (mSignalQuality == null) {
@@ -522,7 +522,7 @@ public class FrontendStatus {
return mSignalQuality;
}
/**
- * Gets Signal Strength in thousandths of a dBm (0.001dBm).
+ * Gets the current Signal Strength in thousandths of a dBm (0.001dBm).
*/
public int getSignalStrength() {
if (mSignalStrength == null) {
@@ -531,7 +531,7 @@ public class FrontendStatus {
return mSignalStrength;
}
/**
- * Gets symbol rate in symbols per second.
+ * Gets the current symbol rate in symbols per second.
*/
public int getSymbolRate() {
if (mSymbolRate == null) {
@@ -540,7 +540,7 @@ public class FrontendStatus {
return mSymbolRate;
}
/**
- * Gets Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
+ * Gets the current Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
* and ETSI EN 302 307-2 V1.1.1.
*/
@FrontendSettings.InnerFec
@@ -551,7 +551,7 @@ public class FrontendStatus {
return mInnerFec;
}
/**
- * Gets modulation.
+ * Gets the currently modulation information.
*/
@FrontendModulation
public int getModulation() {
@@ -561,7 +561,7 @@ public class FrontendStatus {
return mModulation;
}
/**
- * Gets Spectral Inversion for DVBC.
+ * Gets the currently Spectral Inversion information for DVBC.
*/
@FrontendSettings.FrontendSpectralInversion
public int getSpectralInversion() {
@@ -571,7 +571,7 @@ public class FrontendStatus {
return mInversion;
}
/**
- * Gets Power Voltage Type for LNB.
+ * Gets the current Power Voltage Type for LNB.
*/
@Lnb.Voltage
public int getLnbVoltage() {
@@ -581,7 +581,7 @@ public class FrontendStatus {
return mLnbVoltage;
}
/**
- * Gets Physical Layer Pipe ID.
+ * Gets the current Physical Layer Pipe ID.
*/
public int getPlpId() {
if (mPlpId == null) {
@@ -599,7 +599,7 @@ public class FrontendStatus {
return mIsEwbs;
}
/**
- * Gets Automatic Gain Control value which is normalized from 0 to 255.
+ * Gets the current Automatic Gain Control value which is normalized from 0 to 255.
*/
public int getAgc() {
if (mAgc == null) {
@@ -617,7 +617,7 @@ public class FrontendStatus {
return mIsLnaOn;
}
/**
- * Gets Error status by layer.
+ * Gets the current Error information by layer.
*/
@NonNull
public boolean[] getLayerErrors() {
@@ -627,7 +627,7 @@ public class FrontendStatus {
return mIsLayerErrors;
}
/**
- * Gets Modulation Error Ratio in thousandths of a deciBel (0.001dB).
+ * Gets the current Modulation Error Ratio in thousandths of a deciBel (0.001dB).
*/
public int getMer() {
if (mMer == null) {
@@ -636,7 +636,7 @@ public class FrontendStatus {
return mMer;
}
/**
- * Gets frequency difference in Hz.
+ * Gets the current frequency difference in Hz.
*
* <p>Difference between tuning frequency and actual locked frequency.
*/
@@ -647,7 +647,7 @@ public class FrontendStatus {
return mFreqOffset;
}
/**
- * Gets hierarchy Type for DVBT.
+ * Gets the current hierarchy Type for DVBT.
*/
@DvbtFrontendSettings.Hierarchy
public int getHierarchy() {
@@ -657,7 +657,7 @@ public class FrontendStatus {
return mHierarchy;
}
/**
- * Gets lock status for RF.
+ * Gets if the RF is locked or not.
*/
public boolean isRfLocked() {
if (mIsRfLocked == null) {
@@ -666,7 +666,7 @@ public class FrontendStatus {
return mIsRfLocked;
}
/**
- * Gets an array of PLP status for tuned PLPs for ATSC3 frontend.
+ * Gets an array of the current tuned PLPs information of ATSC3 frontend.
*/
@NonNull
public Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo() {
@@ -677,9 +677,9 @@ public class FrontendStatus {
}
/**
- * Gets an array of extended bit error ratio status.
+ * Gets an array of the current extended bit error ratio.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
@@ -693,10 +693,9 @@ public class FrontendStatus {
}
/**
- * Gets an array of code rates status. The {@link FrontendSettings.InnerFec} would be used to
- * show the code rate.
+ * Gets an array of the current code rates.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
@@ -711,9 +710,9 @@ public class FrontendStatus {
}
/**
- * Gets bandwidth status.
+ * Gets the current bandwidth information.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@FrontendBandwidth
@@ -727,9 +726,9 @@ public class FrontendStatus {
}
/**
- * Gets guard interval status.
+ * Gets the current guard interval information.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@FrontendGuardInterval
@@ -743,9 +742,9 @@ public class FrontendStatus {
}
/**
- * Gets tansmission mode status.
+ * Gets the current transmission mode information.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@FrontendTransmissionMode
@@ -759,10 +758,10 @@ public class FrontendStatus {
}
/**
- * Gets the Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP) since the
- * last tune operation.
+ * Gets the current Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP)
+ * since the last tune operation.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
public int getUec() {
@@ -775,9 +774,9 @@ public class FrontendStatus {
}
/**
- * Gets the current DVB-T2 system id status.
+ * Gets the current DVB-T2 system id.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@IntRange(from = 0, to = 0xffff)
@@ -791,10 +790,9 @@ public class FrontendStatus {
}
/**
- * Gets an array of interleaving status. Array value should be within {@link
- * FrontendInterleaveMode}.
+ * Gets an array of the current interleaving mode information.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
@@ -809,9 +807,10 @@ public class FrontendStatus {
}
/**
- * Gets an array of the segments status in ISDB-T Specification of all the channels.
+ * Gets an array of the current segments information in ISDB-T Specification of all the
+ * channels.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
@@ -828,7 +827,7 @@ public class FrontendStatus {
/**
* Gets an array of the Transport Stream Data Rate in BPS of the current channel.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
@@ -842,13 +841,13 @@ public class FrontendStatus {
}
/**
- * Gets an array of the extended modulations status. Array value should be withink {@link
- * FrontendModulation}.
+ * Gets an array of the current extended modulations information.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@NonNull
+ @FrontendModulation
public int[] getExtendedModulations() {
TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_1_1, "getExtendedModulations status");
@@ -859,9 +858,9 @@ public class FrontendStatus {
}
/**
- * Gets roll off status.
+ * Gets the current roll off information.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
@FrontendRollOff
@@ -875,9 +874,9 @@ public class FrontendStatus {
}
/**
- * Gets is MISO enabled status.
+ * Gets is MISO enabled or not.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
public boolean isMisoEnabled() {
@@ -890,9 +889,9 @@ public class FrontendStatus {
}
/**
- * Gets is the Code Rate of the frontend is linear or not status.
+ * Gets is the Code Rate of the frontend is linear or not.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
public boolean isLinear() {
@@ -905,9 +904,9 @@ public class FrontendStatus {
}
/**
- * Gets is the Short Frames enabled or not status.
+ * Gets is the Short Frames enabled or not.
*
- * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
+ * <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker.getTunerVersion()} to check the version.
*/
public boolean isShortFramesEnabled() {
@@ -920,7 +919,7 @@ public class FrontendStatus {
}
/**
- * Status for each tuning Physical Layer Pipes.
+ * Information of each tuning Physical Layer Pipes.
*/
public static class Atsc3PlpTuningInfo {
private final int mPlpId;
diff --git a/media/jni/android_media_MediaCodecLinearBlock.h b/media/jni/android_media_MediaCodecLinearBlock.h
index 8f1d2fa35d70..ae2d3a264abc 100644
--- a/media/jni/android_media_MediaCodecLinearBlock.h
+++ b/media/jni/android_media_MediaCodecLinearBlock.h
@@ -49,7 +49,14 @@ struct JMediaCodecLinearBlock {
if (offset == 0 && size == block.capacity()) {
return mBuffer;
}
- return C2Buffer::CreateLinearBuffer(block.subBlock(offset, size));
+
+ std::shared_ptr<C2Buffer> buffer =
+ C2Buffer::CreateLinearBuffer(block.subBlock(offset, size));
+ for (const std::shared_ptr<const C2Info> &info : mBuffer->info()) {
+ std::shared_ptr<C2Param> param = std::move(C2Param::Copy(*info));
+ buffer->setInfo(std::static_pointer_cast<C2Info>(param));
+ }
+ return buffer;
}
if (mBlock) {
return C2Buffer::CreateLinearBuffer(mBlock->share(offset, size, C2Fence{}));
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 7793d6c45d5e..6db4da95f6db 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -36,6 +36,7 @@ cc_library_shared {
defaults: ["libandroid_defaults"],
srcs: [
+ "activity_manager.cpp",
"asset_manager.cpp",
"choreographer.cpp",
"configuration.cpp",
@@ -95,6 +96,10 @@ cc_library_shared {
include_dirs: ["bionic/libc/dns/include"],
+ local_include_dirs: [ "include_platform", ],
+
+ export_include_dirs: [ "include_platform", ],
+
version_script: "libandroid.map.txt",
stubs: {
symbol_file: "libandroid.map.txt",
diff --git a/native/android/activity_manager.cpp b/native/android/activity_manager.cpp
new file mode 100644
index 000000000000..82f4569b871e
--- /dev/null
+++ b/native/android/activity_manager.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "AActivityManager"
+#include <utils/Log.h>
+
+#include <android/activity_manager.h>
+#include <binder/ActivityManager.h>
+
+namespace android {
+namespace activitymanager {
+
+// Global instance of ActivityManager, service is obtained only on first use.
+static ActivityManager gAm;
+// String tag used with ActivityManager.
+static const String16& getTag() {
+ static String16 tag("libandroid");
+ return tag;
+}
+
+struct UidObserver : public BnUidObserver, public virtual IBinder::DeathRecipient {
+ explicit UidObserver(const AActivityManager_onUidImportance& cb,
+ int32_t cutpoint, void* cookie)
+ : mCallback(cb), mImportanceCutpoint(cutpoint), mCookie(cookie), mRegistered(false) {}
+ bool registerSelf();
+ void unregisterSelf();
+
+ // IUidObserver
+ void onUidGone(uid_t uid, bool disabled) override;
+ void onUidActive(uid_t uid) override;
+ void onUidIdle(uid_t uid, bool disabled) override;
+ void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+ int32_t capability) override;
+
+ // IBinder::DeathRecipient implementation
+ void binderDied(const wp<IBinder>& who) override;
+
+ static int32_t procStateToImportance(int32_t procState);
+ static int32_t importanceToProcState(int32_t importance);
+
+ AActivityManager_onUidImportance mCallback;
+ int32_t mImportanceCutpoint;
+ void* mCookie;
+ std::mutex mRegisteredLock;
+ bool mRegistered GUARDED_BY(mRegisteredLock);
+};
+
+//static
+int32_t UidObserver::procStateToImportance(int32_t procState) {
+ // TODO: remove this after adding Importance to onUidStateChanged callback.
+ if (procState == ActivityManager::PROCESS_STATE_NONEXISTENT) {
+ return AACTIVITYMANAGER_IMPORTANCE_GONE;
+ } else if (procState >= ActivityManager::PROCESS_STATE_HOME) {
+ return AACTIVITYMANAGER_IMPORTANCE_CACHED;
+ } else if (procState == ActivityManager::PROCESS_STATE_HEAVY_WEIGHT) {
+ return AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE;
+ } else if (procState >= ActivityManager::PROCESS_STATE_TOP_SLEEPING) {
+ return AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING;
+ } else if (procState >= ActivityManager::PROCESS_STATE_SERVICE) {
+ return AACTIVITYMANAGER_IMPORTANCE_SERVICE;
+ } else if (procState >= ActivityManager::PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ return AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE;
+ } else if (procState >= ActivityManager::PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ return AACTIVITYMANAGER_IMPORTANCE_VISIBLE;
+ } else if (procState >= ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE) {
+ return AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE;
+ } else {
+ return AACTIVITYMANAGER_IMPORTANCE_FOREGROUND;
+ }
+}
+
+//static
+int32_t UidObserver::importanceToProcState(int32_t importance) {
+ // TODO: remove this after adding Importance to onUidStateChanged callback.
+ if (importance == AACTIVITYMANAGER_IMPORTANCE_GONE) {
+ return ActivityManager::PROCESS_STATE_NONEXISTENT;
+ } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_CACHED) {
+ return ActivityManager::PROCESS_STATE_HOME;
+ } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE) {
+ return ActivityManager::PROCESS_STATE_HEAVY_WEIGHT;
+ } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING) {
+ return ActivityManager::PROCESS_STATE_TOP_SLEEPING;
+ } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_SERVICE) {
+ return ActivityManager::PROCESS_STATE_SERVICE;
+ } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE) {
+ return ActivityManager::PROCESS_STATE_TRANSIENT_BACKGROUND;
+ } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_VISIBLE) {
+ return ActivityManager::PROCESS_STATE_IMPORTANT_FOREGROUND;
+ } else if (importance >= AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE) {
+ return ActivityManager::PROCESS_STATE_FOREGROUND_SERVICE;
+ } else {
+ return ActivityManager::PROCESS_STATE_TOP;
+ }
+}
+
+
+void UidObserver::onUidGone(uid_t uid, bool disabled __unused) {
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (mRegistered && mCallback) {
+ mCallback(uid, AACTIVITYMANAGER_IMPORTANCE_GONE, mCookie);
+ }
+}
+
+void UidObserver::onUidActive(uid_t uid __unused) {}
+
+void UidObserver::onUidIdle(uid_t uid __unused, bool disabled __unused) {}
+
+void UidObserver::onUidStateChanged(uid_t uid, int32_t procState,
+ int64_t procStateSeq __unused,
+ int32_t capability __unused) {
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (mRegistered && mCallback) {
+ mCallback(uid, procStateToImportance(procState), mCookie);
+ }
+}
+
+void UidObserver::binderDied(const wp<IBinder>& /*who*/) {
+ // ActivityManager is dead, try to re-register.
+ {
+ std::scoped_lock lock{mRegisteredLock};
+ // If client already unregistered, don't try to re-register.
+ if (!mRegistered) {
+ return;
+ }
+ // Clear mRegistered to re-register.
+ mRegistered = false;
+ }
+ registerSelf();
+}
+
+bool UidObserver::registerSelf() {
+ std::scoped_lock lock{mRegisteredLock};
+ if (mRegistered) {
+ return true;
+ }
+
+ status_t res = gAm.linkToDeath(this);
+ if (res != OK) {
+ ALOGE("UidObserver: Failed to linkToDeath with ActivityManager (err %d)", res);
+ return false;
+ }
+
+ // TODO: it seems only way to get all changes is to set cutoff to PROCESS_STATE_UNKNOWN.
+ // But there is no equivalent of PROCESS_STATE_UNKNOWN in the UidImportance.
+ // If mImportanceCutpoint is < 0, use PROCESS_STATE_UNKNOWN instead.
+ res = gAm.registerUidObserver(
+ this,
+ ActivityManager::UID_OBSERVER_GONE | ActivityManager::UID_OBSERVER_PROCSTATE,
+ (mImportanceCutpoint < 0) ? ActivityManager::PROCESS_STATE_UNKNOWN
+ : importanceToProcState(mImportanceCutpoint),
+ getTag());
+ if (res != OK) {
+ ALOGE("UidObserver: Failed to register with ActivityManager (err %d)", res);
+ gAm.unlinkToDeath(this);
+ return false;
+ }
+
+ mRegistered = true;
+ ALOGV("UidObserver: Registered with ActivityManager");
+ return true;
+}
+
+void UidObserver::unregisterSelf() {
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (mRegistered) {
+ gAm.unregisterUidObserver(this);
+ gAm.unlinkToDeath(this);
+ mRegistered = false;
+ }
+
+ ALOGV("UidObserver: Unregistered with ActivityManager");
+}
+
+} // activitymanager
+} // android
+
+using namespace android;
+using namespace activitymanager;
+
+struct AActivityManager_UidImportanceListener : public UidObserver {
+};
+
+AActivityManager_UidImportanceListener* AActivityManager_addUidImportanceListener(
+ AActivityManager_onUidImportance onUidImportance, int32_t importanceCutpoint, void* cookie) {
+ sp<UidObserver> observer(new UidObserver(onUidImportance, importanceCutpoint, cookie));
+ if (observer == nullptr || !observer->registerSelf()) {
+ return nullptr;
+ }
+ observer->incStrong((void *)AActivityManager_addUidImportanceListener);
+ return static_cast<AActivityManager_UidImportanceListener*>(observer.get());
+}
+
+void AActivityManager_removeUidImportanceListener(
+ AActivityManager_UidImportanceListener* listener) {
+ if (listener != nullptr) {
+ UidObserver* observer = static_cast<UidObserver*>(listener);
+ observer->unregisterSelf();
+ observer->decStrong((void *)AActivityManager_addUidImportanceListener);
+ }
+}
+
+bool AActivityManager_isUidActive(uid_t uid) {
+ return gAm.isUidActive(uid, getTag());
+}
+
+int32_t AActivityManager_getUidImportance(uid_t uid) {
+ return UidObserver::procStateToImportance(gAm.getUidProcessState(uid, getTag()));
+}
+
diff --git a/native/android/include_platform/android/activity_manager.h b/native/android/include_platform/android/activity_manager.h
new file mode 100644
index 000000000000..0ecd56d512a2
--- /dev/null
+++ b/native/android/include_platform/android/activity_manager.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AACTIVITYMANAGER_H__
+#define __AACTIVITYMANAGER_H__
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+struct AActivityManager_UidImportanceListener;
+typedef struct AActivityManager_UidImportanceListener AActivityManager_UidImportanceListener;
+
+/**
+ * Callback interface when Uid Importance has changed for a uid.
+ *
+ * This callback will be called on an arbitrary thread. Calls to a given listener will be
+ * serialized.
+ *
+ * @param uid the uid for which the importance has changed.
+ * @param uidImportance the new uidImportance for the uid.
+ * @cookie the same cookie when the UidImportanceListener was added.
+ *
+ * Introduced in API 31.
+ */
+typedef void (*AActivityManager_onUidImportance)(uid_t uid, int32_t uidImportance, void* cookie);
+
+/**
+ * ActivityManager Uid Importance constants.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * Constant for Uid Importance: This process is running the
+ * foreground UI; that is, it is the thing currently at the top of the screen
+ * that the user is interacting with.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_FOREGROUND = 100,
+
+ /**
+ * Constant for Uid Importance: This process is running a foreground
+ * service, for example to perform music playback even while the user is
+ * not immediately in the app. This generally indicates that the process
+ * is doing something the user actively cares about.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_FOREGROUND_SERVICE = 125,
+
+ /**
+ * Constant for Uid Importance: This process is running something
+ * that is actively visible to the user, though not in the immediate
+ * foreground. This may be running a window that is behind the current
+ * foreground (so paused and with its state saved, not interacting with
+ * the user, but visible to them to some degree); it may also be running
+ * other services under the system's control that it inconsiders important.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_VISIBLE = 200,
+
+ /**
+ * Constant for Uid Importance: This process is not something the user
+ * is directly aware of, but is otherwise perceptible to them to some degree.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE = 230,
+
+ /**
+ * Constant for Uid Importance: This process contains services
+ * that should remain running. These are background services apps have
+ * started, not something the user is aware of, so they may be killed by
+ * the system relatively freely (though it is generally desired that they
+ * stay running as long as they want to).
+ */
+ AACTIVITYMANAGER_IMPORTANCE_SERVICE = 300,
+
+ /**
+ * Constant for Uid Importance: This process is running the foreground
+ * UI, but the device is asleep so it is not visible to the user. Though the
+ * system will try hard to keep its process from being killed, in all other
+ * ways we consider it a kind of cached process, with the limitations that go
+ * along with that state: network access, running background services, etc.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_TOP_SLEEPING = 325,
+
+ /**
+ * Constant for Uid Importance: This process is running an
+ * application that can not save its state, and thus can't be killed
+ * while in the background. This will be used with apps that have
+ * {@link android.R.attr#cantSaveState} set on their application tag.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_CANT_SAVE_STATE = 350,
+
+ /**
+ * Constant for Uid Importance: This process process contains
+ * cached code that is expendable, not actively running any app components
+ * we care about.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_CACHED = 400,
+
+ /**
+ * Constant for Uid Importance: This process does not exist.
+ */
+ AACTIVITYMANAGER_IMPORTANCE_GONE = 1000,
+};
+
+#if __ANDROID_API__ >= 31
+
+/**
+ * Adds a UidImportanceListener to the ActivityManager.
+ *
+ * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission.
+ *
+ * @param onUidImportance the listener callback that will receive change reports.
+ *
+ * @param importanceCutpoint the level of importance in which the caller is interested
+ * in differences. For example, if AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE is used
+ * here, you will receive a call each time a uid's importance transitions between being
+ * <= AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE and > AACTIVITYMANAGER_IMPORTANCE_PERCEPTIBLE.
+ *
+ * @param cookie a cookie that will be passed back to the listener callback.
+ *
+ * @return an opaque pointer of AActivityManager_UidImportanceListener, or nullptr
+ * upon failure. Upon success, the returned AActivityManager_UidImportanceListener pointer
+ * must be removed and released through AActivityManager_removeUidImportanceListener.
+ */
+AActivityManager_UidImportanceListener* AActivityManager_addUidImportanceListener(
+ AActivityManager_onUidImportance onUidImportance,
+ int32_t importanceCutpoint,
+ void* cookie) __INTRODUCED_IN(31);
+
+/**
+ * Removes a UidImportanceListener that was added with AActivityManager_addUidImportanceListener.
+ *
+ * When this returns, it's guaranteed the listener callback will no longer be invoked.
+ *
+ * @param listener the UidImportanceListener to be removed.
+ */
+void AActivityManager_removeUidImportanceListener(
+ AActivityManager_UidImportanceListener* listener) __INTRODUCED_IN(31);
+
+/**
+ * Queries if a uid is currently active.
+ *
+ * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission.
+ *
+ * @return true if the uid is active, false otherwise.
+ */
+bool AActivityManager_isUidActive(uid_t uid) __INTRODUCED_IN(31);
+
+/**
+ * Queries the current Uid Importance value of a uid.
+ *
+ * This API requires android.Manifest.permission.PACKAGE_USAGE_STATS permission.
+ *
+ * @param uid the uid for which the importance value is queried.
+ * @return the current uid importance value for uid.
+ */
+int32_t AActivityManager_getUidImportance(uid_t uid) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
+__END_DECLS
+
+#endif // __AACTIVITYMANAGER_H__
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index eca67bd7d211..8fa3acf502bc 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -1,5 +1,9 @@
LIBANDROID {
global:
+ AActivityManager_addUidImportanceListener; # apex # introduced=31
+ AActivityManager_removeUidImportanceListener; # apex # introduced=31
+ AActivityManager_isUidActive; # apex # introduced=31
+ AActivityManager_getUidImportance; # apex # introduced=31
AAssetDir_close;
AAssetDir_getNextFileName;
AAssetDir_rewind;
diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
new file mode 100644
index 000000000000..1a51218616d2
--- /dev/null
+++ b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp
@@ -0,0 +1,11 @@
+android_test_helper_app {
+ name: "UidImportanceHelperApp",
+ manifest: "HelperAppManifest.xml",
+ static_libs: ["androidx.test.rules"],
+ sdk_version: "test_current",
+ srcs: ["src/**/*.java"],
+ test_suites: [
+ "general-tests",
+ ],
+}
+
diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml b/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml
new file mode 100644
index 000000000000..3583b33756e9
--- /dev/null
+++ b/native/android/tests/activitymanager/UidImportanceHelperApps/HelperAppManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.UidImportanceHelper"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <application android:label="UidImportanceHelper">
+ <activity android:name="com.android.tests.UidImportanceHelper.MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java b/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java
new file mode 100644
index 000000000000..db0f2b5ed967
--- /dev/null
+++ b/native/android/tests/activitymanager/UidImportanceHelperApps/src/com/android/tests/UidImportanceHelper/MainActivity.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 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.UidImportanceHelper;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This is an empty activity for testing the UID policy of media transcoding service.
+ */
+public class MainActivity extends Activity {
+ private static final String TAG = "MainActivity";
+
+ // Called at the start of the full lifetime.
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Initialize Activity and inflate the UI.
+ }
+
+ // Called after onCreate has finished, use to restore UI state
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ // Restore UI state from the savedInstanceState.
+ // This bundle has also been passed to onCreate.
+ // Will only be called if the Activity has been
+ // killed by the system since it was last visible.
+ }
+
+ // Called before subsequent visible lifetimes
+ // for an activity process.
+ @Override
+ public void onRestart() {
+ super.onRestart();
+ // Load changes knowing that the Activity has already
+ // been visible within this process.
+ }
+
+ // Called at the start of the visible lifetime.
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Apply any required UI change now that the Activity is visible.
+ }
+
+ // Called at the start of the active lifetime.
+ @Override
+ public void onResume() {
+ super.onResume();
+ // Resume any paused UI updates, threads, or processes required
+ // by the Activity but suspended when it was inactive.
+ }
+
+ // Called to save UI state changes at the
+ // end of the active lifecycle.
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ // Save UI state changes to the savedInstanceState.
+ // This bundle will be passed to onCreate and
+ // onRestoreInstanceState if the process is
+ // killed and restarted by the run time.
+ super.onSaveInstanceState(savedInstanceState);
+ }
+
+ // Called at the end of the active lifetime.
+ @Override
+ public void onPause() {
+ // Suspend UI updates, threads, or CPU intensive processes
+ // that don't need to be updated when the Activity isn't
+ // the active foreground Activity.
+ super.onPause();
+ }
+
+ // Called at the end of the visible lifetime.
+ @Override
+ public void onStop() {
+ // Suspend remaining UI updates, threads, or processing
+ // that aren't required when the Activity isn't visible.
+ // Persist all edits or state changes
+ // as after this call the process is likely to be killed.
+ super.onStop();
+ }
+
+ // Sometimes called at the end of the full lifetime.
+ @Override
+ public void onDestroy() {
+ // Clean up any resources including ending threads,
+ // closing database connections etc.
+ super.onDestroy();
+ }
+}
diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp
new file mode 100644
index 000000000000..d4b5015ad8f3
--- /dev/null
+++ b/native/android/tests/activitymanager/nativeTests/Android.bp
@@ -0,0 +1,39 @@
+cc_test {
+ name: "ActivityManagerNativeTestCases",
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["src/ActivityManagerNativeTest.cpp"],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libandroid",
+ "libbinder",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libgtest",
+ ],
+ stl: "c++_shared",
+
+ test_suites: [
+ "general-tests",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ required: [
+ "UidImportanceHelperApp",
+ ],
+}
diff --git a/native/android/tests/activitymanager/nativeTests/AndroidTest.xml b/native/android/tests/activitymanager/nativeTests/AndroidTest.xml
new file mode 100644
index 000000000000..bf6287ad4883
--- /dev/null
+++ b/native/android/tests/activitymanager/nativeTests/AndroidTest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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="Config for ActivityManager native test cases">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <!-- Force root to allow registering UidImportanceListener from native test -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+ <!-- Install the helper apk -->
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="UidImportanceHelperApp.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="ActivityManagerNativeTestCases->/data/local/tmp/ActivityManagerNativeTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="ActivityManagerNativeTestCases" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+
+ <!-- Controller that will skip the module if a native bridge situation is detected -->
+ <!-- For example: module wants to run arm and device is x86 -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
+</configuration>
diff --git a/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp b/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp
new file mode 100644
index 000000000000..75ba0ffb229a
--- /dev/null
+++ b/native/android/tests/activitymanager/nativeTests/src/ActivityManagerNativeTest.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ActivityManagerNativeTest"
+
+#include <android-base/logging.h>
+#include <android/activity_manager.h>
+#include <binder/PermissionController.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+
+constexpr const char* kTestPackage = "com.android.tests.UidImportanceHelper";
+constexpr const char* kTestActivity = "com.android.tests.UidImportanceHelper.MainActivity";
+constexpr int64_t kEventTimeoutUs = 500000;
+
+//-----------------------------------------------------------------
+class ActivityManagerNativeTest : public ::testing::Test {
+protected:
+ ActivityManagerNativeTest() : mUidObserver(nullptr), mTestAppUid(-1), mLastUidImportance(-1) {}
+
+ virtual ~ActivityManagerNativeTest() {}
+
+ /* Test setup*/
+ virtual void SetUp() { android::ProcessState::self()->startThreadPool(); }
+
+ /* Test tear down */
+ virtual void TearDown() {}
+
+ bool waitForImportance(int32_t val, int64_t timeoutUs) {
+ std::unique_lock lock(mLock);
+
+ if (mLastUidImportance != val && timeoutUs > 0) {
+ mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
+ }
+
+ return mLastUidImportance == val;
+ }
+
+ void onUidImportanceChanged(uid_t uid, int32_t uidImportance) {
+ LOG(ERROR) << "OnUidImportance: uid " << uid << ", importance " << uidImportance;
+ std::unique_lock lock(mLock);
+
+ if (uid == mTestAppUid) {
+ mLastUidImportance = uidImportance;
+ mCondition.notify_one();
+ }
+ }
+
+ static void OnUidImportance(uid_t uid, int32_t uidImportance, void* cookie) {
+ ActivityManagerNativeTest* owner = reinterpret_cast<ActivityManagerNativeTest*>(cookie);
+ owner->onUidImportanceChanged(uid, uidImportance);
+ }
+
+ AActivityManager_UidImportanceListener* mUidObserver;
+ uid_t mTestAppUid;
+ std::mutex mLock;
+ std::condition_variable mCondition;
+ int32_t mLastUidImportance;
+};
+
+static bool getUidForPackage(const char* packageName, /*inout*/ uid_t& uid) {
+ android::PermissionController pc;
+ uid = pc.getPackageUid(android::String16(packageName), 0);
+ if (uid <= 0) {
+ ALOGE("Unknown package: '%s'", packageName);
+ return false;
+ }
+ return true;
+}
+
+struct ShellHelper {
+ static bool RunCmd(const std::string& cmdStr) {
+ int ret = system(cmdStr.c_str());
+ if (ret != 0) {
+ LOG(ERROR) << "Failed to run cmd: " << cmdStr << ", exitcode " << ret;
+ return false;
+ }
+ return true;
+ }
+
+ static bool Start(const char* packageName, const char* activityName) {
+ return RunCmd("am start -W " + std::string(packageName) + "/" + std::string(activityName) +
+ " &> /dev/null");
+ }
+
+ static bool Stop(const char* packageName) {
+ return RunCmd("am force-stop " + std::string(packageName));
+ }
+};
+
+//-------------------------------------------------------------------------------------------------
+TEST_F(ActivityManagerNativeTest, testUidImportance) {
+ pid_t selfPid = ::getpid();
+ uid_t selfUid = ::getuid();
+
+ uid_t testAppUid;
+ EXPECT_TRUE(getUidForPackage(kTestPackage, testAppUid));
+ LOG(INFO) << "testUidImportance: uidselfUid" << selfUid << ", selfPid " << selfPid
+ << ", testAppUid " << testAppUid;
+ mTestAppUid = testAppUid;
+
+ // Expect the initial UidImportance to be GONE.
+ EXPECT_FALSE(AActivityManager_isUidActive(testAppUid));
+ EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), AACTIVITYMANAGER_IMPORTANCE_GONE);
+
+ mUidObserver = AActivityManager_addUidImportanceListener(&OnUidImportance,
+ AACTIVITYMANAGER_IMPORTANCE_FOREGROUND,
+ (void*)this);
+ EXPECT_TRUE(mUidObserver != nullptr);
+
+ // Start the test activity, and expect to receive UidImportance change to FOREGROUND.
+ EXPECT_TRUE(ShellHelper::Start(kTestPackage, kTestActivity));
+ EXPECT_TRUE(waitForImportance(AACTIVITYMANAGER_IMPORTANCE_FOREGROUND, kEventTimeoutUs));
+ EXPECT_TRUE(AActivityManager_isUidActive(testAppUid));
+ EXPECT_EQ(AActivityManager_getUidImportance(testAppUid),
+ AACTIVITYMANAGER_IMPORTANCE_FOREGROUND);
+
+ // Stop the test activity, and expect to receive UidImportance change to GONE.
+ EXPECT_TRUE(ShellHelper::Stop(kTestPackage));
+ EXPECT_TRUE(waitForImportance(AACTIVITYMANAGER_IMPORTANCE_GONE, kEventTimeoutUs));
+ EXPECT_FALSE(AActivityManager_isUidActive(testAppUid));
+ EXPECT_EQ(AActivityManager_getUidImportance(testAppUid), AACTIVITYMANAGER_IMPORTANCE_GONE);
+
+ AActivityManager_removeUidImportanceListener(mUidObserver);
+ mUidObserver = nullptr;
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 11d1b0a9ef2a..087275e73ee8 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -322,6 +322,11 @@ public class ExternalStorageProvider extends FileSystemProvider {
return true;
}
+ if (TextUtils.equals(Environment.DIRECTORY_ANDROID.toLowerCase(),
+ path.toLowerCase())) {
+ return true;
+ }
+
return false;
} catch (IOException e) {
throw new IllegalArgumentException(
diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
index 12d21cae48d4..1c0e718a17b2 100644
--- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
+++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
@@ -84,8 +84,9 @@ public class EmergencyNumberUtils {
}
private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) {
- Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(
- categories);
+ // TODO(b/171542607): Use platform API when its bug is fixed.
+ Map<Integer, List<EmergencyNumber>> allLists = filterEmergencyNumbersByCategories(
+ mTelephonyManager.getEmergencyNumberList(), categories);
if (allLists == null || allLists.isEmpty()) {
Log.w(TAG, "Unable to retrieve emergency number lists!");
return new ArrayList<>();
@@ -130,4 +131,28 @@ public class EmergencyNumberUtils {
}
return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId());
}
+
+ /**
+ * Filter emergency numbers with categories.
+ */
+ private Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
+ Map<Integer, List<EmergencyNumber>> emergencyNumberList, int categories) {
+ Map<Integer, List<EmergencyNumber>> filteredMap = new ArrayMap<>();
+ if (emergencyNumberList == null) {
+ return filteredMap;
+ }
+ for (Integer subscriptionId : emergencyNumberList.keySet()) {
+ List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
+ subscriptionId);
+ List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
+ for (EmergencyNumber number : allNumbersForSub) {
+ if (number.isInEmergencyServiceCategories(categories)) {
+ numbersForCategoriesPerSub.add(number);
+ }
+ }
+ filteredMap.put(
+ subscriptionId, numbersForCategoriesPerSub);
+ }
+ return filteredMap;
+ }
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9f1c96d88905..22213cba217d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -995,8 +995,14 @@
<!-- Settings item title for media transcoding settings. [CHAR LIMIT=85] -->
<string name="transcode_settings_title">Media transcoding settings</string>
- <!-- Settings item title to disable transcoding globally. [CHAR LIMIT=85] -->
- <string name="transcode_enable_all">Disable transcoding</string>
+ <!-- Settings item title to enable user's control over further transcoding preferences. [CHAR LIMIT=85] -->
+ <string name="transcode_user_control">Override transcoding defaults</string>
+
+ <!-- Settings item title to enable transcoding globally. [CHAR LIMIT=85] -->
+ <string name="transcode_enable_all">Enable transcoding</string>
+
+ <!-- Settings item title to select the default behavior for transcoding if an encodig is not supported by an app. [CHAR LIMIT=85] -->
+ <string name="transcode_default">Assume apps support modern formats</string>
<!-- Settings category title for selecting apps to be enabled for transcoding. [CHAR LIMIT=85] -->
<string name="transcode_skip_apps">Enable transcoding for apps</string>
@@ -1110,7 +1116,7 @@
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until charged</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charge been limited -->
- <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Battery limited temporarily</string>
+ <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Optimizing for battery health</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index f9bb90e9616a..b3205d7563b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -16,6 +16,7 @@
package com.android.settingslib.fuelgauge;
+import static android.os.BatteryManager.BATTERY_HEALTH_OVERHEAT;
import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -128,6 +129,15 @@ public class BatteryStatus {
}
/**
+ * Whether battery is overheated.
+ *
+ * @return true if battery is overheated
+ */
+ public boolean isOverheated() {
+ return health == BATTERY_HEALTH_OVERHEAT;
+ }
+
+ /**
* Return current chargin speed is fast, slow or normal.
*
* @return the charing speed
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java
index 183651f77f5a..e7d870ee75da 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/DiscreteValueValidator.java
@@ -34,6 +34,6 @@ public final class DiscreteValueValidator implements Validator {
@Override
public boolean validate(@Nullable String value) {
- return ArrayUtils.contains(mValues, value);
+ return value == null || ArrayUtils.contains(mValues, value);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index a02d67fd7bca..668e26751b0c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -34,10 +34,6 @@ import java.util.Map;
* Validators for Global settings
*/
public class GlobalSettingsValidators {
- /**
- * All settings in {@link Global.SETTINGS_TO_BACKUP} array *must* have a non-null validator,
- * otherwise they won't be restored.
- */
public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
static {
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java
index 1a0b88c1c150..58620b5f4c8f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveFloatRangeValidator.java
@@ -34,6 +34,9 @@ final class InclusiveFloatRangeValidator implements Validator {
@Override
public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return true;
+ }
try {
final float floatValue = Float.parseFloat(value);
return floatValue >= mMin && floatValue <= mMax;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
index f9f8ce851719..aa27c877a786 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
@@ -34,6 +34,9 @@ final class InclusiveIntegerRangeValidator implements Validator {
@Override
public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return true;
+ }
try {
final int intValue = Integer.parseInt(value);
return intValue >= mMin && intValue <= mMax;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 9331b5e09f38..517fa54cd805 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -42,11 +42,6 @@ import java.util.Map;
* Validators for the Secure Settings.
*/
public class SecureSettingsValidators {
- /**
- * All settings in {@link Secure.SETTINGS_TO_BACKUP} and {@link
- * DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP} array *must* have a non-null
- * validator, otherwise they won't be restored.
- */
public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
static {
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 8d5c6e69b850..97e1d6848af6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -16,6 +16,7 @@
package android.provider.settings.validators;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.net.Uri;
@@ -48,6 +49,9 @@ public class SettingsValidators {
public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
@Override
public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return true;
+ }
try {
return Integer.parseInt(value) >= 0;
} catch (NumberFormatException e) {
@@ -59,6 +63,9 @@ public class SettingsValidators {
public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
@Override
public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return true;
+ }
try {
Integer.parseInt(value);
return true;
@@ -71,6 +78,9 @@ public class SettingsValidators {
public static final Validator URI_VALIDATOR = new Validator() {
@Override
public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return true;
+ }
try {
Uri.decode(value);
return true;
@@ -87,7 +97,7 @@ public class SettingsValidators {
*/
public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
@Override
- public boolean validate(@Nullable String value) {
+ public boolean validate(@NonNull String value) {
return value != null && ComponentName.unflattenFromString(value) != null;
}
};
@@ -104,18 +114,15 @@ public class SettingsValidators {
public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
@Override
- public boolean validate(@Nullable String value) {
+ public boolean validate(@NonNull String value) {
return value != null && isStringPackageName(value);
}
- private boolean isStringPackageName(String value) {
+ private boolean isStringPackageName(@NonNull String value) {
// The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
// and underscores ('_'). However, individual package name parts may only
// start with letters.
// (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
- if (value == null) {
- return false;
- }
String[] subparts = value.split("\\.");
boolean isValidPackageName = true;
for (String subpart : subparts) {
@@ -143,7 +150,7 @@ public class SettingsValidators {
@Override
public boolean validate(@Nullable String value) {
if (value == null) {
- return false;
+ return true;
}
return value.length() <= MAX_IPV6_LENGTH;
}
@@ -153,7 +160,7 @@ public class SettingsValidators {
@Override
public boolean validate(@Nullable String value) {
if (value == null) {
- return false;
+ return true;
}
Locale[] validLocales = Locale.getAvailableLocales();
for (Locale locale : validLocales) {
@@ -167,6 +174,9 @@ public class SettingsValidators {
/** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
+ if (value == null) {
+ return true;
+ }
if (TextUtils.isEmpty(value)) {
return false;
}
@@ -184,6 +194,9 @@ public class SettingsValidators {
static final Validator DATE_FORMAT_VALIDATOR = value -> {
try {
+ if (value == null) {
+ return true;
+ }
new SimpleDateFormat(value);
return true;
} catch (IllegalArgumentException | NullPointerException e) {
@@ -211,6 +224,9 @@ public class SettingsValidators {
static final Validator NONE_NEGATIVE_LONG_VALIDATOR = new Validator() {
@Override
public boolean validate(String value) {
+ if (value == null) {
+ return true;
+ }
try {
return Long.parseLong(value) >= 0;
} catch (NumberFormatException e) {
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index c5d4fa9f1b40..d278c5974ae6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -38,12 +38,6 @@ import java.util.Map;
* Validators for System settings
*/
public class SystemSettingsValidators {
- /**
- * These are all public system settings
- *
- * <p>All settings in {@link System.SETTINGS_TO_BACKUP} array *must* have a non-null validator,
- * otherwise they won't be restored.
- */
@UnsupportedAppUsage
public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
@@ -89,15 +83,7 @@ public class SystemSettingsValidators {
return value == null || value.length() < MAX_LENGTH;
}
});
- VALIDATORS.put(
- System.FONT_SCALE,
- value -> {
- try {
- return Float.parseFloat(value) >= 0;
- } catch (NumberFormatException | NullPointerException e) {
- return false;
- }
- });
+ VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f));
VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(
System.DISPLAY_COLOR_MODE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index b061df1423ba..40b0fcff3aac 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -496,11 +496,14 @@ final class SettingsState {
public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues,
String packageName) {
List<String> changedKeys = new ArrayList<>();
+ final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator();
// Delete old keys with the prefix that are not part of the new set.
- for (int i = 0; i < mSettings.keySet().size(); ++i) {
- String key = mSettings.keyAt(i);
- if (key.startsWith(prefix) && !keyValues.containsKey(key)) {
- Setting oldState = mSettings.remove(key);
+ while (iterator.hasNext()) {
+ Map.Entry<String, Setting> entry = iterator.next();
+ final String key = entry.getKey();
+ final Setting oldState = entry.getValue();
+ if (key != null && key.startsWith(prefix) && !keyValues.containsKey(key)) {
+ iterator.remove();
FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key,
/* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 48c0dc4fb2b8..c740bac4d0b4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -146,7 +146,6 @@ public class SettingsBackupTest {
Settings.Global.BROADCAST_OFFLOAD_CONSTANTS,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
- Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
Settings.Global.BATTERY_STATS_CONSTANTS,
Settings.Global.BINDER_CALLS_STATS,
@@ -230,9 +229,8 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV,
Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR,
- Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS,
+ Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH,
Settings.Global.DEVICE_DEMO_MODE,
- Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS,
Settings.Global.BATTERY_SAVER_CONSTANTS,
Settings.Global.BATTERY_TIP_CONSTANTS,
Settings.Global.DEFAULT_SM_DP_PLUS,
diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
index 9134d875097e..e5d148cb213a 100644
--- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -56,8 +56,8 @@ public class SettingsValidatorsTest {
}
@Test
- public void testNonNegativeIntegerValidator_onNullValue_returnsFalse() {
- assertFalse(SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR.validate(null));
+ public void testNonNegativeIntegerValidator_onNullValue_returnsTrue() {
+ assertTrue(SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR.validate(null));
}
@Test
@@ -69,8 +69,8 @@ public class SettingsValidatorsTest {
}
@Test
- public void testAnyIntegerValidator_onNullValue_returnsFalse() {
- assertFalse(SettingsValidators.ANY_INTEGER_VALIDATOR.validate(null));
+ public void testAnyIntegerValidator_onNullValue_returnsTrue() {
+ assertTrue(SettingsValidators.ANY_INTEGER_VALIDATOR.validate(null));
}
@Test
@@ -91,8 +91,8 @@ public class SettingsValidatorsTest {
}
@Test
- public void testLenientIpAddressValidator_onNullValue_returnsFalse() {
- assertFalse(SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR.validate(null));
+ public void testLenientIpAddressValidator_onNullValue_returnsTrue() {
+ assertTrue(SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR.validate(null));
}
@Test
@@ -120,8 +120,8 @@ public class SettingsValidatorsTest {
}
@Test
- public void testLocaleValidator_onNullValue_returnsFalse() {
- assertFalse(SettingsValidators.LOCALE_VALIDATOR.validate(null));
+ public void testLocaleValidator_onNullValue_returnsTrue() {
+ assertTrue(SettingsValidators.LOCALE_VALIDATOR.validate(null));
}
@Test
@@ -149,11 +149,11 @@ public class SettingsValidatorsTest {
}
@Test
- public void testDiscreteValueValidator_onNullValue_returnsFalse() {
+ public void testDiscreteValueValidator_onNullValue_returnsTrue() {
String[] discreteTypes = new String[]{"Type1", "Type2"};
Validator v = new DiscreteValueValidator(discreteTypes);
- assertFalse(v.validate(null));
+ assertTrue(v.validate(null));
}
@Test
@@ -167,10 +167,10 @@ public class SettingsValidatorsTest {
}
@Test
- public void testInclusiveIntegerRangeValidator_onNullValue_returnsFalse() {
+ public void testInclusiveIntegerRangeValidator_onNullValue_returnsTrue() {
Validator v = new InclusiveIntegerRangeValidator(0, 5);
- assertFalse(v.validate(null));
+ assertTrue(v.validate(null));
}
@Test
@@ -184,10 +184,10 @@ public class SettingsValidatorsTest {
}
@Test
- public void testInclusiveFloatRangeValidator_onNullValue_returnsFalse() {
+ public void testInclusiveFloatRangeValidator_onNullValue_returnsTrue() {
Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
- assertFalse(v.validate(null));
+ assertTrue(v.validate(null));
}
@Test
@@ -220,8 +220,8 @@ public class SettingsValidatorsTest {
}
@Test
- public void dateFormatValidator_onNullValue_returnsFalse() {
- assertFalse(SettingsValidators.DATE_FORMAT_VALIDATOR.validate(null));
+ public void dateFormatValidator_onNullValue_returnsTrue() {
+ assertTrue(SettingsValidators.DATE_FORMAT_VALIDATOR.validate(null));
}
@Test
@@ -240,8 +240,8 @@ public class SettingsValidatorsTest {
}
@Test
- public void testJSONObjectValidator_onNullValue_returnsFalse() {
- assertFalse(SettingsValidators.JSON_OBJECT_VALIDATOR.validate(null));
+ public void testJSONObjectValidator_onNullValue_returnsTrue() {
+ assertTrue(SettingsValidators.JSON_OBJECT_VALIDATOR.validate(null));
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2e3ea24f62e2..141b8cbe8022 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -332,6 +332,9 @@
<!-- Permission needed for CTS test - DisplayTest -->
<uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" />
+ <!-- Permission needed for CTS test - MatchContentFrameRateTest -->
+ <uses-permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" />
+
<!-- Permission needed for CTS test - TimeManagerTest -->
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
@@ -344,9 +347,18 @@
<!-- Permissions required for CTS test - NotificationManagerTest -->
<uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
+ <!-- Permissions required for CTS test - CtsContactsProviderTestCases -->
+ <uses-permission android:name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
+
+ <!-- Permissions required for CTS test - CarrierMessagingServiceWrapprTest -->
+ <uses-permission android:name="android.permission.BIND_CARRIER_SERVICES" />
+
<!-- Allows overriding the system's device state from the shell -->
<uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
+ <!-- Permissions required for CTS tests to close system dialogs -->
+ <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 52b41a43c63e..2da958f6b8b9 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -516,20 +516,6 @@
android:excludeFromRecents="true"
android:visibleToInstantApps="true"/>
- <!-- started from PipController -->
- <activity
- android:name="com.android.wm.shell.pip.tv.PipMenuActivity"
- android:permission="com.android.systemui.permission.SELF"
- android:exported="false"
- android:theme="@style/PipTheme"
- android:launchMode="singleTop"
- android:taskAffinity=""
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|locale|layoutDirection"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- androidprv:alwaysFocusable="true"
- android:excludeFromRecents="true" />
-
<!-- started from TvNotificationPanel -->
<activity
android:name=".statusbar.tv.notifications.TvNotificationPanelActivity"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 6c6c9270bc84..3058d9466121 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -127,6 +127,13 @@ public interface ClockPlugin extends Plugin {
default void onTimeZoneChanged(TimeZone timeZone) {}
/**
+ * Notifies that the time format has changed.
+ *
+ * @param timeFormat "12" for 12-hour format, "24" for 24-hour format
+ */
+ default void onTimeFormatChanged(String timeFormat) {}
+
+ /**
* Indicates whether the keyguard status area (date) should be shown below
* the clock.
*/
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index c82bda6620bd..19f82480284e 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -73,13 +73,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="right"
- android:textSize="80dp"
+ android:textSize="100dp"
android:letterSpacing="0.02"
android:lineSpacingMultiplier=".8"
android:includeFontPadding="false"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
+ dozeWeight="200"
+ lockScreenWeight="300"
/>
</FrameLayout>
<FrameLayout
@@ -95,13 +97,15 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
- android:textSize="180dp"
+ android:textSize="200dp"
android:letterSpacing="0.02"
android:lineSpacingMultiplier=".8"
android:includeFontPadding="false"
android:fontFamily="@font/clock"
android:typeface="monospace"
android:elegantTextHeight="false"
+ dozeWeight="100"
+ lockScreenWeight="400"
/>
</FrameLayout>
<include layout="@layout/keyguard_status_area"
diff --git a/packages/SystemUI/res-keyguard/values/attrs.xml b/packages/SystemUI/res-keyguard/values/attrs.xml
index 293b683497f8..bfcc56cdc660 100644
--- a/packages/SystemUI/res-keyguard/values/attrs.xml
+++ b/packages/SystemUI/res-keyguard/values/attrs.xml
@@ -39,4 +39,9 @@
</declare-styleable>
<attr name="passwordStyle" format="reference" />
+
+ <declare-styleable name="AnimatableClockView">
+ <attr name="dozeWeight" format="integer" />
+ <attr name="lockScreenWeight" format="integer" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index d95ab374a5cb..4b6621379b44 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -79,8 +79,8 @@
is not fully charged, and it's plugged into a slow charger, say that it's charging slowly. -->
<string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
- <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's limited temporarily. -->
- <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Battery limited temporarily</string>
+ <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's optimizing for battery health. -->
+ <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Optimizing for battery health</string>
<!-- When the lock screen is showing and the battery is low, warn user to plug
in the phone soon. -->
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index bc48e8fe3eea..71a1cc292ec9 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -137,5 +137,4 @@
<item name="android:shadowColor">@color/keyguard_shadow_color</item>
<item name="android:shadowRadius">?attr/shadowRadius</item>
</style>
-
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 01b55b70d5ad..880dd378e390 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -357,6 +357,9 @@
the notification is not swiped enough to dismiss it. -->
<bool name="config_showNotificationGear">true</bool>
+ <!-- Whether or not a background should be drawn behind a notification. -->
+ <bool name="config_drawNotificationBackground">true</bool>
+
<!-- Whether or the notifications can be shown and dismissed with a drag. -->
<bool name="config_enableNotificationShadeDrag">true</bool>
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 3157a5a8fc5b..7c6a27446c78 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -24,6 +24,8 @@ import com.android.settingslib.Utils;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.util.ViewController;
+import java.util.TimeZone;
+
/**
* Controls the color of a GradientTextClock.
*/
@@ -62,12 +64,26 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
}
/**
- * Updates the time for this view.
+ * Updates the time for the view.
*/
public void refreshTime() {
mView.refreshTime();
}
+ /**
+ * Updates the timezone for the view.
+ */
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ mView.onTimeZoneChanged(timeZone);
+ }
+
+ /**
+ * Trigger a time format update
+ */
+ public void refreshFormat() {
+ mView.refreshFormat();
+ }
+
private void initColors() {
mLockScreenColors[0] = Utils.getColorAttrDefaultColor(getContext(),
com.android.systemui.R.attr.wallpaperTextColor);
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index 6d1d42e2988b..ca99563986b4 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -19,13 +19,17 @@ package com.android.keyguard;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.icu.text.DateTimePatternGenerator;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.widget.TextView;
+import com.android.systemui.R;
+
import java.util.Calendar;
+import java.util.TimeZone;
import kotlin.Unit;
@@ -38,11 +42,14 @@ public class AnimatableClockView extends TextView {
private static final CharSequence FORMAT_24_HOUR = "HH\nmm";
private static final long ANIM_DURATION = 300;
+ private final Calendar mTime = Calendar.getInstance();
+
private CharSequence mFormat;
private CharSequence mDescFormat;
- private Calendar mTime = Calendar.getInstance();
private int[] mDozingColors;
private int[] mLockScreenColors;
+ private final int mDozingWeight;
+ private final int mLockScreenWeight;
private TextAnimator mTextAnimator = null;
private Runnable mOnTextAnimatorInitialized;
@@ -62,13 +69,21 @@ public class AnimatableClockView extends TextView {
public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- updateTimeFormat();
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes);
+ try {
+ mDozingWeight = ta.getInt(R.styleable.AnimatableClockView_dozeWeight, 100);
+ mLockScreenWeight = ta.getInt(R.styleable.AnimatableClockView_lockScreenWeight, 300);
+ } finally {
+ ta.recycle();
+ }
+ refreshFormat();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- updateTimeFormat();
+ refreshFormat();
}
@Override
@@ -82,6 +97,11 @@ public class AnimatableClockView extends TextView {
setContentDescription(DateFormat.format(mDescFormat, mTime));
}
+ void onTimeZoneChanged(TimeZone timeZone) {
+ mTime.setTimeZone(timeZone);
+ refreshFormat();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -113,7 +133,7 @@ public class AnimatableClockView extends TextView {
}
void animateDoze(boolean isDozing, boolean animate) {
- setTextStyle(isDozing ? 100 : 300 /* weight */,
+ setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */,
-1,
isDozing ? mDozingColors : mLockScreenColors,
animate);
@@ -144,10 +164,11 @@ public class AnimatableClockView extends TextView {
}
}
- private void updateTimeFormat() {
+ void refreshFormat() {
final boolean use24HourFormat = DateFormat.is24HourFormat(getContext());
mFormat = use24HourFormat ? FORMAT_24_HOUR : FORMAT_12_HOUR;
mDescFormat = getBestDateTimePattern(getContext(), use24HourFormat ? "Hm" : "hm");
+ refreshTime();
}
private static String getBestDateTimePattern(Context context, String skeleton) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 68405a1fca68..ab7ba8a0f358 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -156,13 +156,17 @@ public class KeyguardClockSwitch extends RelativeLayout {
statusAreaLP.removeRule(RelativeLayout.BELOW);
statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
+ statusAreaLP.addRule(RelativeLayout.START_OF, R.id.new_lockscreen_clock_view);
+ statusAreaLP.width = 0;
} else {
setPaddingRelative(0, 0, 0, 0);
mSmallClockFrame.setVisibility(VISIBLE);
mNewLockscreenClockFrame.setVisibility(GONE);
statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
+ statusAreaLP.removeRule(RelativeLayout.START_OF);
statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
+ statusAreaLP.width = ViewGroup.LayoutParams.WRAP_CONTENT;
}
requestLayout();
@@ -401,6 +405,17 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
}
+ /**
+ * Notifies that the time format has changed.
+ *
+ * @param timeFormat "12" for 12-hour format, "24" for 24-hour format
+ */
+ public void onTimeFormatChanged(String timeFormat) {
+ if (mClockPlugin != null) {
+ mClockPlugin.onTimeFormatChanged(timeFormat);
+ }
+ }
+
void updateColors(ColorExtractor.GradientColors colors) {
mSupportsDarkText = colors.supportsDarkText();
mColorPalette = colors.getColorPalette();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 829ff9771fb4..4d6e8a90e57b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -17,7 +17,9 @@
package com.android.keyguard;
import android.app.WallpaperManager;
+import android.content.ContentResolver;
import android.content.res.Resources;
+import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.TypedValue;
import android.view.View;
@@ -90,6 +92,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
};
private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
+ private String mTimeFormat;
@Inject
public KeyguardClockSwitchController(
@@ -98,7 +101,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor, ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
- NotificationIconAreaController notificationIconAreaController) {
+ NotificationIconAreaController notificationIconAreaController,
+ ContentResolver contentResolver) {
super(keyguardClockSwitch);
mResources = resources;
mStatusBarStateController = statusBarStateController;
@@ -106,6 +110,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mClockManager = clockManager;
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
+ mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
}
/**
@@ -246,12 +251,26 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
void updateTimeZone(TimeZone timeZone) {
mView.onTimeZoneChanged(timeZone);
+ if (mNewLockScreenClockViewController != null) {
+ mNewLockScreenClockViewController.onTimeZoneChanged(timeZone);
+ mNewLockScreenLargeClockViewController.onTimeZoneChanged(timeZone);
+ }
}
- void refreshFormat() {
+ void refreshFormat(String timeFormat) {
+ mTimeFormat = timeFormat;
Patterns.update(mResources);
mView.setFormat12Hour(Patterns.sClockView12);
mView.setFormat24Hour(Patterns.sClockView24);
+ mView.onTimeFormatChanged(mTimeFormat);
+ if (mNewLockScreenClockViewController != null) {
+ mNewLockScreenClockViewController.refreshFormat();
+ mNewLockScreenLargeClockViewController.refreshFormat();
+ }
+ }
+
+ void refreshFormat() {
+ refreshFormat(mTimeFormat);
}
float getClockTextTopPadding() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index bc81a198c7e6..826020c71159 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -329,6 +329,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
@Override
+ public void onTimeFormatChanged(String timeFormat) {
+ mKeyguardClockSwitchController.refreshFormat(timeFormat);
+ }
+
+ @Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (showing) {
if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a7e51951e9cd..2071cfaf2bd9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -182,6 +182,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final int MSG_USER_REMOVED = 341;
private static final int MSG_KEYGUARD_GOING_AWAY = 342;
private static final int MSG_LOCK_SCREEN_MODE = 343;
+ private static final int MSG_TIME_FORMAT_UPDATE = 344;
public static final int LOCK_SCREEN_MODE_NORMAL = 0;
public static final int LOCK_SCREEN_MODE_LAYOUT_1 = 1;
@@ -280,6 +281,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mCallbacks = Lists.newArrayList();
private ContentObserver mDeviceProvisionedObserver;
private ContentObserver mLockScreenModeObserver;
+ private ContentObserver mTimeFormatChangeObserver;
private boolean mSwitchingUser;
@@ -1721,6 +1723,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
case MSG_LOCK_SCREEN_MODE:
handleLockScreenMode();
break;
+ case MSG_TIME_FORMAT_UPDATE:
+ handleTimeFormatUpdate((String) msg.obj);
+ break;
default:
super.handleMessage(msg);
break;
@@ -1866,12 +1871,25 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SHOW_NEW_LOCKSCREEN),
false, mLockScreenModeObserver);
+
+ mTimeFormatChangeObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_TIME_FORMAT_UPDATE,
+ Settings.System.getString(
+ mContext.getContentResolver(),
+ Settings.System.TIME_12_24)));
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.TIME_12_24),
+ false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
}
private void updateLockScreenMode() {
final int newMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_NEW_LOCKSCREEN,
- isUdfpsEnrolled() ? 1 : 0);
+ Settings.Global.SHOW_NEW_LOCKSCREEN, LOCK_SCREEN_MODE_LAYOUT_1);
if (newMode != mLockScreenMode) {
mLockScreenMode = newMode;
mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE);
@@ -2451,6 +2469,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
+ * Handle (@line #MSG_TIME_FORMAT_UPDATE}
+ *
+ * @param timeFormat "12" for 12-hour format, "24" for 24-hour format
+ */
+ private void handleTimeFormatUpdate(String timeFormat) {
+ Assert.isMainThread();
+ if (DEBUG) Log.d(TAG, "handleTimeFormatUpdate timeFormat=" + timeFormat);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onTimeFormatChanged(timeFormat);
+ }
+ }
+ }
+
+ /**
* Handle {@link #MSG_BATTERY_UPDATE}
*/
private void handleBatteryUpdate(BatteryStatus status) {
@@ -2687,6 +2721,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return true;
}
+ // change in battery overheat
+ if (current.health != old.health) {
+ return true;
+ }
+
return false;
}
@@ -3055,6 +3094,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mContext.getContentResolver().unregisterContentObserver(mLockScreenModeObserver);
}
+ if (mTimeFormatChangeObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver);
+ }
+
try {
ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index b722deab528a..36617c239352 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -58,6 +58,11 @@ public class KeyguardUpdateMonitorCallback {
public void onTimeZoneChanged(TimeZone timeZone) { }
/**
+ * Called when time format changes.
+ */
+ public void onTimeFormatChanged(String timeFormat) { }
+
+ /**
* Called when the carrier PLMN or SPN changes.
*/
public void onRefreshCarrierInfo() { }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 7127f26a7ed2..275bfaa1023c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -30,7 +30,6 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.util.DisplayMetrics;
import android.view.Choreographer;
import android.view.IWindowManager;
import android.view.LayoutInflater;
@@ -141,16 +140,6 @@ public class DependencyProvider {
return networkController.getDataSaverController();
}
- /** */
- @Provides
- @SysUISingleton
- public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) {
- DisplayMetrics displayMetrics = new DisplayMetrics();
- context.getDisplay().getMetrics(displayMetrics);
- return displayMetrics;
- }
-
- /** */
@Provides
@SysUISingleton
public INotificationManager provideINotificationManager() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index d683a74aedcc..a89c7acea984 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -24,7 +24,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
-import com.android.wm.shell.animation.FlingAnimationUtils;
import javax.inject.Singleton;
@@ -51,15 +50,12 @@ import dagger.Provides;
GlobalConcurrencyModule.class})
public class GlobalModule {
- // TODO(b/162923491): This should not be a singleton at all, the display metrics can change and
- // callers should be creating a new builder on demand
- @Singleton
+ /** */
@Provides
- static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
- Context context) {
+ public DisplayMetrics provideDisplayMetrics(Context context) {
DisplayMetrics displayMetrics = new DisplayMetrics();
context.getDisplay().getMetrics(displayMetrics);
- return new FlingAnimationUtils.Builder(displayMetrics);
+ return displayMetrics;
}
/** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
new file mode 100644
index 000000000000..6050c2b90e34
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 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.screenshot;
+
+@SuppressWarnings("PointlessBooleanExpression")
+class LogConfig {
+
+ /** Log ALL the things... */
+ private static final boolean DEBUG_ALL = false;
+
+ /** Default log logTag for screenshot code */
+ private static final String TAG_SS = "Screenshot";
+
+ /** Use class name as Log tag instead of the default */
+ private static final boolean TAG_WITH_CLASS_NAME = false;
+
+ /** Action creation and user selection: Share, Save, Edit, Delete, Smart action, etc */
+ static final boolean DEBUG_ACTIONS = DEBUG_ALL || false;
+
+ /** Debug info about animations such as start, complete and cancel */
+ static final boolean DEBUG_ANIM = DEBUG_ALL || false;
+
+ /** Whenever Uri is supplied to consumer, or onComplete runnable is run() */
+ static final boolean DEBUG_CALLBACK = DEBUG_ALL || false;
+
+ /** Logs information about dismissing the screenshot tool */
+ static final boolean DEBUG_DISMISS = DEBUG_ALL || false;
+
+ /** Touch or key event driven action or side effects */
+ static final boolean DEBUG_INPUT = DEBUG_ALL || false;
+
+ /** Scroll capture usage */
+ static final boolean DEBUG_SCROLL = DEBUG_ALL || false;
+
+ /** Service lifecycle events and callbacks */
+ static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
+
+ /** Storage related actions, Bitmap.compress, ContentManager, etc */
+ static final boolean DEBUG_STORAGE = DEBUG_ALL || false;
+
+ /** High level logical UI actions: timeout, onConfigChanged, insets, show actions, reset */
+ static final boolean DEBUG_UI = DEBUG_ALL || false;
+
+ /** Interactions with Window and WindowManager */
+ static final boolean DEBUG_WINDOW = DEBUG_ALL || false;
+
+ static String logTag(Class<?> cls) {
+ return TAG_WITH_CLASS_NAME ? cls.getSimpleName() : TAG_SS;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index b2ebf3f700b9..f4ce77acb8ec 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -16,6 +16,11 @@
package com.android.systemui.screenshot;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_STORAGE;
+import static com.android.systemui.screenshot.LogConfig.logTag;
+
import android.app.ActivityTaskManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -45,7 +50,7 @@ import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.text.TextUtils;
import android.text.format.DateUtils;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -73,8 +78,8 @@ import java.util.concurrent.CompletableFuture;
/**
* An AsyncTask that saves an image to the media store in the background.
*/
-class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
- private static final String TAG = "SaveImageInBackgroundTask";
+class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
+ private static final String TAG = logTag(SaveImageInBackgroundTask.class);
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
@@ -121,6 +126,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... paramsUnused) {
if (isCancelled()) {
+ if (DEBUG_STORAGE) {
+ Log.d(TAG, "cancelled! returning null");
+ }
return null;
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
@@ -151,9 +159,19 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
try {
// First, write the actual data for our screenshot
try (OutputStream out = resolver.openOutputStream(uri)) {
+ if (DEBUG_STORAGE) {
+ Log.d(TAG, "Compressing PNG:"
+ + " w=" + image.getWidth() + " h=" + image.getHeight());
+ }
if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
+ if (DEBUG_STORAGE) {
+ Log.d(TAG, "Bitmap.compress returned false");
+ }
throw new IOException("Failed to compress");
}
+ if (DEBUG_STORAGE) {
+ Log.d(TAG, "Done compressing PNG");
+ }
}
// Next, write metadata to help index the screenshot
@@ -181,7 +199,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
DateTimeFormatter.ofPattern("XXX").format(time));
}
-
+ if (DEBUG_STORAGE) {
+ Log.d(TAG, "Writing EXIF metadata");
+ }
exif.saveAttributes();
}
@@ -190,6 +210,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
values.put(MediaColumns.IS_PENDING, 0);
values.putNull(MediaColumns.DATE_EXPIRES);
resolver.update(uri, values, null, null);
+ if (DEBUG_STORAGE) {
+ Log.d(TAG, "Completed writing to ContentManager");
+ }
} catch (Exception e) {
resolver.delete(uri, null);
throw e;
@@ -215,15 +238,24 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri);
mParams.mActionsReadyListener.onActionsReady(mImageData);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) "
+ + "finisher.accept(\"" + mImageData.uri + "\"");
+ }
mParams.finisher.accept(mImageData.uri);
mParams.image = null;
} catch (Exception e) {
// IOException/UnsupportedOperationException may be thrown if external storage is
// not mounted
- Slog.e(TAG, "unable to save screenshot", e);
+ if (DEBUG_STORAGE) {
+ Log.d(TAG, "Failed to store screenshot", e);
+ }
mParams.clearImage();
mImageData.reset();
mParams.mActionsReadyListener.onActionsReady(mImageData);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)");
+ }
mParams.finisher.accept(null);
}
@@ -245,6 +277,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// params from the ctor in any case.
mImageData.reset();
mParams.mActionsReadyListener.onActionsReady(mImageData);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "onCancelled, calling (Consumer<Uri>) finisher.accept(null)");
+ }
mParams.finisher.accept(null);
mParams.clearImage();
}
@@ -380,7 +415,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
try {
return ActivityTaskManager.getService().getLastResumedActivityUserId();
} catch (RemoteException e) {
- Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e);
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, "Failed to get UserHandle of foreground app: ", e);
+ }
return context.getUserId();
}
}
@@ -421,6 +458,4 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
.putExtra(ScreenshotController.EXTRA_ID, screenshotId)
.putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
}
-
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index d7f9c6163b1d..6a4e93be0fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -21,6 +21,14 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_UI;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW;
+import static com.android.systemui.screenshot.LogConfig.logTag;
+
import static java.util.Objects.requireNonNull;
import android.animation.Animator;
@@ -75,6 +83,7 @@ import javax.inject.Inject;
* Controls the state and flow for screenshots.
*/
public class ScreenshotController {
+ private static final String TAG = logTag(ScreenshotController.class);
/**
* POD used in the AsyncTask which saves an image in the background.
*/
@@ -110,12 +119,10 @@ public class ScreenshotController {
}
}
- abstract static class ActionsReadyListener {
- abstract void onActionsReady(ScreenshotController.SavedImageData imageData);
+ interface ActionsReadyListener {
+ void onActionsReady(ScreenshotController.SavedImageData imageData);
}
- private static final String TAG = "ScreenshotController";
-
// These strings are used for communicating the action invoked to
// ScreenshotNotificationSmartActionsProvider.
static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
@@ -166,6 +173,9 @@ public class ScreenshotController {
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_CORNER_TIMEOUT:
+ if (DEBUG_UI) {
+ Log.d(TAG, "Corner timeout hit");
+ }
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
ScreenshotController.this.dismissScreenshot(false);
break;
@@ -292,13 +302,17 @@ public class ScreenshotController {
* Clears current screenshot
*/
void dismissScreenshot(boolean immediate) {
+ if (DEBUG_DISMISS) {
+ Log.d(TAG, "dismissScreenshot(immediate=" + immediate + ")");
+ }
// If we're already animating out, don't restart the animation
// (but do obey an immediate dismissal)
if (!immediate && mScreenshotView.isDismissing()) {
- Log.v(TAG, "Already dismissing, ignoring duplicate command");
+ if (DEBUG_DISMISS) {
+ Log.v(TAG, "Already dismissing, ignoring duplicate command");
+ }
return;
}
- Log.v(TAG, "Clearing screenshot");
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
if (immediate) {
resetScreenshotView();
@@ -308,12 +322,17 @@ public class ScreenshotController {
}
/**
- * Update assets (called when the dark theme status changes). We only need to update the dismiss
- * button and the actions container background, since the buttons are re-inflated on demand.
+ * Update resources on configuration change. Reinflate for theme/color changes.
*/
private void reloadAssets() {
+ if (DEBUG_UI) {
+ Log.d(TAG, "reloadAssets()");
+ }
boolean wasAttached = mDecorView.isAttachedToWindow();
if (wasAttached) {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "Removing screenshot window");
+ }
mWindowManager.removeView(mDecorView);
}
@@ -336,6 +355,9 @@ public class ScreenshotController {
// TODO(159460485): Remove this when focus is handled properly in the system
mScreenshotView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "onTouch: ACTION_OUTSIDE");
+ }
// Once the user touches outside, stop listening for input
setWindowFocusable(false);
}
@@ -344,6 +366,9 @@ public class ScreenshotController {
mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "onKeyEvent: KeyEvent.KEYCODE_BACK");
+ }
dismissScreenshot(false);
return true;
}
@@ -373,10 +398,16 @@ public class ScreenshotController {
Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
if (screenshot == null) {
- Log.e(TAG, "Screenshot bitmap was null");
+ Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "Supplying null to Consumer<Uri>");
+ }
finisher.accept(null);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "Calling mOnCompleteRunnable.run()");
+ }
mOnCompleteRunnable.run();
return;
}
@@ -399,12 +430,17 @@ public class ScreenshotController {
if (!mScreenshotView.isDismissing()) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
}
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
+ + "(dismissing=" + mScreenshotView.isDismissing() + ")");
+ }
mScreenshotView.reset();
}
mScreenBitmap = screenshot;
if (!isUserSetupComplete()) {
+ Log.w(TAG, "User setup not complete, displaying toast only");
// User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
// and sharing shouldn't be exposed to the user.
saveScreenshotAndToast(finisher);
@@ -416,6 +452,9 @@ public class ScreenshotController {
mScreenBitmap.prepareToDraw();
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+ if (DEBUG_UI) {
+ Log.d(TAG, "saveScreenshot: reloading assets");
+ }
reloadAssets();
}
@@ -450,25 +489,21 @@ public class ScreenshotController {
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
});
- saveScreenshotInWorkerThread(finisher,
- new ScreenshotController.ActionsReadyListener() {
- @Override
- void onActionsReady(ScreenshotController.SavedImageData imageData) {
- finisher.accept(imageData.uri);
- if (imageData.uri == null) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_save_text);
- } else {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
-
- mScreenshotHandler.post(() -> {
- Toast.makeText(mContext, R.string.screenshot_saved_title,
- Toast.LENGTH_SHORT).show();
- });
- }
- }
- });
+ saveScreenshotInWorkerThread(finisher, imageData -> {
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri);
+ }
+ finisher.accept(imageData.uri);
+ if (imageData.uri == null) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_save_text);
+ } else {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+ mScreenshotHandler.post(() -> Toast.makeText(mContext,
+ R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
+ }
+ });
}
/**
@@ -479,37 +514,46 @@ public class ScreenshotController {
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
mScreenshotHandler.post(() -> {
if (!mScreenshotView.isAttachedToWindow()) {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "Adding screenshot window");
+ }
mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams);
}
mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets);
mScreenshotHandler.post(() -> {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "adding OnComputeInternalInsetsListener");
+ }
mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(
mScreenshotView);
mScreenshotAnimation =
mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
- saveScreenshotInWorkerThread(finisher,
- new ScreenshotController.ActionsReadyListener() {
- @Override
- void onActionsReady(
- ScreenshotController.SavedImageData imageData) {
- showUiOnActionsReady(imageData);
- }
- });
+ saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
// Play the shutter sound to notify that we've taken a screenshot
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "starting post-screenshot animation");
+ }
mScreenshotAnimation.start();
});
});
}
+ /** Reset screenshot view and then call onCompleteRunnable */
private void resetScreenshotView() {
+ if (DEBUG_UI) {
+ Log.d(TAG, "resetScreenshotView");
+ }
if (mScreenshotView.isAttachedToWindow()) {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "Removing screenshot window");
+ }
mWindowManager.removeView(mDecorView);
}
mScreenshotView.reset();
@@ -519,8 +563,7 @@ public class ScreenshotController {
/**
* Creates a new worker thread and saves the screenshot to the media store.
*/
- private void saveScreenshotInWorkerThread(
- Consumer<Uri> finisher,
+ private void saveScreenshotInWorkerThread(Consumer<Uri> finisher,
@Nullable ScreenshotController.ActionsReadyListener actionsReadyListener) {
ScreenshotController.SaveImageInBackgroundData
data = new ScreenshotController.SaveImageInBackgroundData();
@@ -530,13 +573,7 @@ public class ScreenshotController {
if (mSaveInBgTask != null) {
// just log success/failure for the pre-existing screenshot
- mSaveInBgTask.setActionsReadyListener(
- new ScreenshotController.ActionsReadyListener() {
- @Override
- void onActionsReady(ScreenshotController.SavedImageData imageData) {
- logSuccessOnActionsReady(imageData);
- }
- });
+ mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
@@ -555,6 +592,9 @@ public class ScreenshotController {
SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ if (DEBUG_UI) {
+ Log.d(TAG, "Showing UI actions, dismiss timeout: " + timeoutMs + " ms");
+ }
mScreenshotHandler.sendMessageDelayed(
mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
timeoutMs);
@@ -600,6 +640,9 @@ public class ScreenshotController {
* shown.
*/
private void setWindowFocusable(boolean focusable) {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setWindowFocusable: " + focusable);
+ }
if (focusable) {
mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
} else {
@@ -618,9 +661,10 @@ public class ScreenshotController {
if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
|| bitmap.getHeight() == 0) {
- Log.e(TAG, String.format(
- "Provided bitmap and insets create degenerate region: %dx%d %s",
- bitmap.getWidth(), bitmap.getHeight(), bitmapInsets));
+ if (DEBUG_UI) {
+ Log.e(TAG, "Provided bitmap and insets create degenerate region: "
+ + bitmap.getWidth() + "x" + bitmap.getHeight() + " " + bitmapInsets);
+ }
return false;
}
@@ -628,11 +672,10 @@ public class ScreenshotController {
float boundsAspect = ((float) screenBounds.width()) / screenBounds.height();
boolean matchWithinTolerance = Math.abs(insettedBitmapAspect - boundsAspect) < 0.1f;
- if (!matchWithinTolerance) {
- Log.d(TAG, String.format("aspectRatiosMatch: don't match bitmap: %f, bounds: %f",
- insettedBitmapAspect, boundsAspect));
+ if (DEBUG_UI) {
+ Log.d(TAG, "aspectRatiosMatch: don't match bitmap: " + insettedBitmapAspect
+ + ", bounds: " + boundsAspect);
}
-
return matchWithinTolerance;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
index 63f323ed2768..29f67f348f7b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -16,6 +16,9 @@
package com.android.systemui.screenshot;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
+import static com.android.systemui.screenshot.LogConfig.logTag;
+
import android.app.Notification;
import android.content.ComponentName;
import android.graphics.Bitmap;
@@ -32,6 +35,9 @@ import java.util.concurrent.CompletableFuture;
* in order to provide smart actions in the screenshot notification.
*/
public class ScreenshotNotificationSmartActionsProvider {
+
+ private static final String TAG = logTag(ScreenshotNotificationSmartActionsProvider.class);
+
/* Key provided in the notification action to get the type of smart action. */
public static final String ACTION_TYPE = "action_type";
public static final String DEFAULT_ACTION_TYPE = "Smart Action";
@@ -51,29 +57,21 @@ public class ScreenshotNotificationSmartActionsProvider {
ERROR,
TIMEOUT
}
-
- private static final String TAG = "ScreenshotActions";
-
/**
* Default implementation that returns an empty list.
* This method is overridden in vendor-specific Sys UI implementation.
*
- * @param screenshotId A generated random unique id for the screenshot.
- * @param screenshotFileName name of the file where the screenshot will be written.
- * @param bitmap The bitmap of the screenshot. The bitmap config must be {@link
- * HARDWARE}.
- * @param componentName Contains package and activity class names where the screenshot was
- * taken. This is used as an additional signal to generate and rank
- * more relevant actions.
- * @param userHandle The user handle of the app where the screenshot was taken.
+ * @param screenshotId a unique id for the screenshot
+ * @param screenshotUri uri where the screenshot has been stored
+ * @param bitmap the screenshot, config must be {@link Bitmap.Config#HARDWARE}
+ * @param componentName name of the foreground component when the screenshot was taken
+ * @param userHandle user handle of the foreground task owner
*/
- public CompletableFuture<List<Notification.Action>> getActions(
- String screenshotId,
- Uri screenshotUri,
- Bitmap bitmap,
- ComponentName componentName,
- UserHandle userHandle) {
- Log.d(TAG, "Returning empty smart action list.");
+ public CompletableFuture<List<Notification.Action>> getActions(String screenshotId,
+ Uri screenshotUri, Bitmap bitmap, ComponentName componentName, UserHandle userHandle) {
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, "Returning empty smart action list.");
+ }
return CompletableFuture.completedFuture(Collections.emptyList());
}
@@ -88,7 +86,9 @@ public class ScreenshotNotificationSmartActionsProvider {
*/
public void notifyOp(String screenshotId, ScreenshotOp op, ScreenshotOpStatus status,
long durationMs) {
- Log.d(TAG, "Return without notify.");
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, "SmartActions: notifyOp() - return without notify");
+ }
}
/**
@@ -100,6 +100,8 @@ public class ScreenshotNotificationSmartActionsProvider {
* @param isSmartAction whether action invoked was a smart action.
*/
public void notifyAction(String screenshotId, String action, boolean isSmartAction) {
- Log.d(TAG, "Return without notify.");
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, "SmartActions: notifyAction: return without notify");
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 468602a5369e..1184dc7fe1a4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -18,6 +18,9 @@ package com.android.systemui.screenshot;
import static android.os.AsyncTask.THREAD_POOL_EXECUTOR;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
+import static com.android.systemui.screenshot.LogConfig.logTag;
+
import android.app.ActivityManager;
import android.app.Notification;
import android.content.ComponentName;
@@ -27,7 +30,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.SystemUIFactory;
@@ -47,7 +50,7 @@ import javax.inject.Inject;
*/
@SysUISingleton
public class ScreenshotSmartActions {
- private static final String TAG = "ScreenshotSmartActions";
+ private static final String TAG = logTag(ScreenshotSmartActions.class);
@Inject
public ScreenshotSmartActions() {}
@@ -57,18 +60,24 @@ public class ScreenshotSmartActions {
String screenshotId, Uri screenshotUri, Bitmap image,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
boolean smartActionsEnabled, UserHandle userHandle) {
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, String.format("getSmartActionsFuture id=%s, uri=%s, provider=%s, "
+ + "smartActionsEnabled=%b, userHandle=%s", screenshotId, screenshotUri,
+ smartActionsProvider.getClass(), smartActionsEnabled, userHandle));
+ }
if (!smartActionsEnabled) {
- Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list.");
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, "Screenshot Intelligence not enabled, returning empty list.");
+ }
return CompletableFuture.completedFuture(Collections.emptyList());
}
if (image.getConfig() != Bitmap.Config.HARDWARE) {
- Slog.w(TAG, String.format(
- "Bitmap expected: Hardware, Bitmap found: %s. Returning empty list.",
- image.getConfig()));
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. "
+ + "Returning empty list.", image.getConfig()));
+ }
return CompletableFuture.completedFuture(Collections.emptyList());
}
-
- Slog.d(TAG, "Screenshot from user profile: " + userHandle.getIdentifier());
CompletableFuture<List<Notification.Action>> smartActionsFuture;
long startTimeMs = SystemClock.uptimeMillis();
try {
@@ -83,7 +92,9 @@ public class ScreenshotSmartActions {
} catch (Throwable e) {
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
- Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e);
+ if (DEBUG_ACTIONS) {
+ Log.e(TAG, "Failed to get future for screenshot notification smart actions.", e);
+ }
notifyScreenshotOp(screenshotId, smartActionsProvider,
ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
@@ -97,12 +108,18 @@ public class ScreenshotSmartActions {
CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
long startTimeMs = SystemClock.uptimeMillis();
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, String.format("getSmartActions id=%s, timeoutMs=%d, provider=%s",
+ screenshotId, timeoutMs, smartActionsProvider.getClass()));
+ }
try {
List<Notification.Action> actions = smartActionsFuture.get(timeoutMs,
TimeUnit.MILLISECONDS);
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
- Slog.d(TAG, String.format("Got %d smart actions. Wait time: %d ms",
- actions.size(), waitTimeMs));
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms",
+ actions.size(), waitTimeMs));
+ }
notifyScreenshotOp(screenshotId, smartActionsProvider,
ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
@@ -110,8 +127,10 @@ public class ScreenshotSmartActions {
return actions;
} catch (Throwable e) {
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
- Slog.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", waitTimeMs),
- e);
+ if (DEBUG_ACTIONS) {
+ Log.e(TAG, String.format("Error getting smart actions. Wait time: %d ms",
+ waitTimeMs), e);
+ }
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status =
(e instanceof TimeoutException)
? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
@@ -127,10 +146,14 @@ public class ScreenshotSmartActions {
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, String.format("%s notifyOp: %s id=%s, status=%s, durationMs=%d",
+ smartActionsProvider.getClass(), op, screenshotId, status, durationMs));
+ }
try {
smartActionsProvider.notifyOp(screenshotId, op, status, durationMs);
} catch (Throwable e) {
- Slog.e(TAG, "Error in notifyScreenshotOp: ", e);
+ Log.e(TAG, "Error in notifyScreenshotOp: ", e);
}
}
@@ -140,9 +163,13 @@ public class ScreenshotSmartActions {
ScreenshotNotificationSmartActionsProvider provider =
SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
context, THREAD_POOL_EXECUTOR, new Handler());
+ if (DEBUG_ACTIONS) {
+ Log.e(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b",
+ provider.getClass(), action, screenshotId, isSmartAction));
+ }
provider.notifyAction(screenshotId, action, isSmartAction);
} catch (Throwable e) {
- Slog.e(TAG, "Error in notifyScreenshotAction: ", e);
+ Log.e(TAG, "Error in notifyScreenshotAction: ", e);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 3814bd2999e9..cce60f9c7c9d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -18,6 +18,14 @@ package com.android.systemui.screenshot;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_INPUT;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_UI;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_WINDOW;
+import static com.android.systemui.screenshot.LogConfig.logTag;
+
import static java.util.Objects.requireNonNull;
import android.animation.Animator;
@@ -75,7 +83,7 @@ import java.util.function.Consumer;
public class ScreenshotView extends FrameLayout implements
ViewTreeObserver.OnComputeInternalInsetsListener {
- private static final String TAG = "ScreenshotView";
+ private static final String TAG = logTag(ScreenshotView.class);
private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
@@ -166,12 +174,18 @@ public class ScreenshotView extends FrameLayout implements
* @param onClick the action to take when the chip is clicked.
*/
public void showScrollChip(Runnable onClick) {
+ if (DEBUG_SCROLL) {
+ Log.d(TAG, "Showing Scroll option");
+ }
mScrollChip.setVisibility(VISIBLE);
- mScrollChip.setOnClickListener((v) ->
- onClick.run()
- // TODO Logging, store event consumer to a field
- //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED);
- );
+ mScrollChip.setOnClickListener((v) -> {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "scroll chip tapped");
+ }
+ onClick.run();
+ // TODO Logging, store event consumer to a field
+ //onElementTapped.accept(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED);
+ });
}
@Override // ViewTreeObserver.OnComputeInternalInsetsListener
@@ -179,15 +193,13 @@ public class ScreenshotView extends FrameLayout implements
inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
Region touchRegion = new Region();
- Rect screenshotRect = new Rect();
- mScreenshotPreview.getBoundsOnScreen(screenshotRect);
- touchRegion.op(screenshotRect, Region.Op.UNION);
- Rect actionsRect = new Rect();
- mActionsContainer.getBoundsOnScreen(actionsRect);
- touchRegion.op(actionsRect, Region.Op.UNION);
- Rect dismissRect = new Rect();
- mDismissButton.getBoundsOnScreen(dismissRect);
- touchRegion.op(dismissRect, Region.Op.UNION);
+ final Rect tmpRect = new Rect();
+ mScreenshotPreview.getBoundsOnScreen(tmpRect);
+ touchRegion.op(tmpRect, Region.Op.UNION);
+ mActionsContainer.getBoundsOnScreen(tmpRect);
+ touchRegion.op(tmpRect, Region.Op.UNION);
+ mDismissButton.getBoundsOnScreen(tmpRect);
+ touchRegion.op(tmpRect, Region.Op.UNION);
if (QuickStepContract.isGesturalMode(mNavMode)) {
// Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
@@ -362,7 +374,6 @@ public class ScreenshotView extends FrameLayout implements
toCorner.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
mScreenshotPreview.setVisibility(View.VISIBLE);
}
});
@@ -380,8 +391,13 @@ public class ScreenshotView extends FrameLayout implements
dropInAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "drop-in animation completed");
+ }
mDismissButton.setOnClickListener(view -> {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "dismiss button clicked");
+ }
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
animateDismissal();
});
@@ -497,7 +513,7 @@ public class ScreenshotView extends FrameLayout implements
try {
imageData.editAction.actionIntent.send();
} catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Intent cancelled", e);
+ Log.e(TAG, "PendingIntent was cancelled", e);
}
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
animateDismissal();
@@ -543,6 +559,9 @@ public class ScreenshotView extends FrameLayout implements
}
private void animateDismissal(Animator dismissAnimation) {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "removing OnComputeInternalInsetsListener");
+ }
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
mDismissAnimation = dismissAnimation;
mDismissAnimation.addListener(new AnimatorListenerAdapter() {
@@ -551,6 +570,9 @@ public class ScreenshotView extends FrameLayout implements
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "Cancelled dismiss animation");
+ }
mCancelled = true;
}
@@ -558,17 +580,33 @@ public class ScreenshotView extends FrameLayout implements
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (!mCancelled) {
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "after dismiss animation, calling onDismissRunnable.run()");
+ }
mOnDismissRunnable.run();
}
}
});
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "Starting dismiss animation");
+ }
mDismissAnimation.start();
}
void reset() {
+ if (DEBUG_UI) {
+ Log.d(TAG, "reset screenshot view");
+ }
+
if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "cancelling dismiss animation");
+ }
mDismissAnimation.cancel();
}
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "removing OnComputeInternalInsetsListener");
+ }
// Make sure we clean up the view tree observer
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
// Clear any references to the bitmap
@@ -637,10 +675,9 @@ public class ScreenshotView extends FrameLayout implements
BitmapDrawable bitmapDrawable = new BitmapDrawable(res, bitmap);
if (insettedHeight == 0 || insettedWidth == 0 || bitmap.getWidth() == 0
|| bitmap.getHeight() == 0) {
- Log.e(TAG, String.format(
- "Can't create insetted drawable, using 0 insets "
- + "bitmap and insets create degenerate region: %dx%d %s",
- bitmap.getWidth(), bitmap.getHeight(), insets));
+ Log.e(TAG, "Can't create inset drawable, using 0 insets bitmap and insets create "
+ + "degenerate region: " + bitmap.getWidth() + "x" + bitmap.getHeight() + " "
+ + bitmapDrawable);
return bitmapDrawable;
}
@@ -689,12 +726,18 @@ public class ScreenshotView extends FrameLayout implements
} else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
if (isPastDismissThreshold()
&& (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
+ if (DEBUG_INPUT) {
+ Log.d(TAG, "dismiss triggered via swipe gesture");
+ }
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED);
animateDismissal(createSwipeDismissAnimation());
return true;
} else if (MathUtils.dist(mStartX, mStartY, event.getRawX(), event.getRawY())
> dpToPx(CLICK_MOVEMENT_THRESHOLD_DP)) {
// if we've moved a non-negligible distance (but not past the threshold),
+ if (DEBUG_DISMISS) {
+ Log.d(TAG, "swipe gesture abandoned");
+ }
// start the return animation
if ((mDismissAnimation == null || !mDismissAnimation.isRunning())) {
createSwipeReturnAnimation().start();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index ea835fa94fe8..e159992bc9a5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -16,6 +16,8 @@
package com.android.systemui.screenshot;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL;
+
import static java.util.Objects.requireNonNull;
import android.annotation.UiContext;
@@ -49,10 +51,7 @@ public class ScrollCaptureClient {
@VisibleForTesting
static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
- private static final String TAG = "ScrollCaptureClient";
-
- /** Whether to log method names and arguments for most calls */
- private static final boolean DEBUG_TRACE = false;
+ private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);
/**
* A connection to a remote window. Starts a capture session.
@@ -155,7 +154,7 @@ public class ScrollCaptureClient {
*/
public void request(int displayId, int taskId, Consumer<Connection> consumer) {
try {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken
+ ", taskId=" + taskId + ", consumer=" + consumer + ")");
}
@@ -189,7 +188,7 @@ public class ScrollCaptureClient {
@Override
public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds,
Point positionInWindow) throws RemoteException {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds
+ ", positionInWindow=" + positionInWindow + ")");
}
@@ -202,7 +201,7 @@ public class ScrollCaptureClient {
@Override
public void onUnavailable() throws RemoteException {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "onUnavailable");
}
// The targeted app does not support scroll capture
@@ -211,7 +210,7 @@ public class ScrollCaptureClient {
@Override
public void onCaptureStarted() {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "onCaptureStarted()");
}
mSessionConsumer.accept(this);
@@ -224,7 +223,7 @@ public class ScrollCaptureClient {
if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) {
image = mReader.acquireNextImage();
}
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber
+ ", contentArea=" + contentArea + ") image=" + image);
}
@@ -237,7 +236,7 @@ public class ScrollCaptureClient {
@Override
public void onConnectionClosed() {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "onConnectionClosed()");
}
disconnect();
@@ -261,7 +260,7 @@ public class ScrollCaptureClient {
// -> Error handling: BiConsumer<Session, Throwable> ?
@Override
public void start(int maxBufferCount, Consumer<Session> sessionConsumer) {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "start(maxBufferCount=" + maxBufferCount
+ ", sessionConsumer=" + sessionConsumer + ")");
}
@@ -288,7 +287,7 @@ public class ScrollCaptureClient {
@Override
public void end(Runnable listener) {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "end(listener=" + listener + ")");
}
if (mStarted) {
@@ -319,7 +318,7 @@ public class ScrollCaptureClient {
@Override
public void requestTile(Rect contentRect, Consumer<CaptureResult> consumer) {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "requestTile(contentRect=" + contentRect + "consumer=" + consumer + ")");
}
mRequestRect = new Rect(contentRect);
@@ -337,7 +336,7 @@ public class ScrollCaptureClient {
*/
@Override
public void binderDied() {
- if (DEBUG_TRACE) {
+ if (DEBUG_SCROLL) {
Log.d(TAG, "binderDied()");
}
disconnect();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
index f32529fdaf04..3ad922b57c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -16,6 +16,7 @@
package com.android.systemui.screenshot;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
@@ -26,7 +27,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
-import android.util.Slog;
import javax.inject.Inject;
@@ -48,7 +48,9 @@ public class SmartActionsReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
- Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
+ if (DEBUG_ACTIONS) {
+ Log.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
+ }
ActivityOptions opts = ActivityOptions.makeBasic();
try {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 4aaea8041abd..c2b20d37f3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -20,6 +20,10 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
+import static com.android.systemui.screenshot.LogConfig.logTag;
import android.app.Service;
import android.content.BroadcastReceiver;
@@ -53,17 +57,21 @@ import java.util.function.Consumer;
import javax.inject.Inject;
public class TakeScreenshotService extends Service {
- private static final String TAG = "TakeScreenshotService";
+ private static final String TAG = logTag(TakeScreenshotService.class);
private final ScreenshotController mScreenshot;
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
private final ScreenshotNotificationsController mNotificationsController;
private final Handler mHandler;
+
private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
+ if (DEBUG_DISMISS) {
+ Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
+ }
mScreenshot.dismissScreenshot(false);
}
}
@@ -73,6 +81,9 @@ public class TakeScreenshotService extends Service {
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
UiEventLogger uiEventLogger,
ScreenshotNotificationsController notificationsController) {
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "new " + this);
+ }
mHandler = new Handler(Looper.getMainLooper(), this::handleMessage);
mScreenshot = screenshotController;
mUserManager = userManager;
@@ -81,13 +92,27 @@ public class TakeScreenshotService extends Service {
}
@Override
+ public void onCreate() {
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "onCreate()");
+ }
+ }
+
+ @Override
public IBinder onBind(@NonNull Intent intent) {
registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS));
- return new Messenger(mHandler).getBinder();
+ final Messenger m = new Messenger(mHandler);
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "onBind: returning connection: " + m);
+ }
+ return m.getBinder();
}
@Override
public boolean onUnbind(Intent intent) {
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "onUnbind");
+ }
if (mScreenshot != null) {
mScreenshot.dismissScreenshot(true);
}
@@ -95,6 +120,14 @@ public class TakeScreenshotService extends Service {
return false;
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "onDestroy");
+ }
+ }
+
/** Respond to incoming Message via Binder (Messenger) */
private boolean handleMessage(Message msg) {
final Messenger replyTo = msg.replyTo;
@@ -108,7 +141,13 @@ public class TakeScreenshotService extends Service {
Log.w(TAG, "Skipping screenshot because storage is locked!");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_save_user_locked_text);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "handleMessage: calling uriConsumer.accept(null)");
+ }
uriConsumer.accept(null);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "handleMessage: calling onComplete.run()");
+ }
onComplete.run();
return true;
}
@@ -120,12 +159,21 @@ public class TakeScreenshotService extends Service {
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
+ }
mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");
+ }
mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
+ if (DEBUG_SERVICE) {
+ Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
+ }
Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
screenshotRequest.getBitmapBundle());
Rect screenBounds = screenshotRequest.getBoundsInScreen();
@@ -145,7 +193,9 @@ public class TakeScreenshotService extends Service {
private void sendComplete(Messenger target) {
try {
- Log.d(TAG, "sendComplete: " + target);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "sendComplete: " + target);
+ }
target.send(Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE));
} catch (RemoteException e) {
Log.d(TAG, "ignored remote exception", e);
@@ -154,7 +204,9 @@ public class TakeScreenshotService extends Service {
private void reportUri(Messenger target, Uri uri) {
try {
- Log.d(TAG, "reportUri: " + target + " -> " + uri);
+ if (DEBUG_CALLBACK) {
+ Log.d(TAG, "reportUri: " + target + " -> " + uri);
+ }
target.send(Message.obtain(null, SCREENSHOT_MSG_URI, uri));
} catch (RemoteException e) {
Log.d(TAG, "ignored remote exception", e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 3765e5a26e8e..5259aa968825 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -117,6 +117,8 @@ public class KeyguardIndicationController implements StateListener,
private boolean mPowerPluggedIn;
private boolean mPowerPluggedInWired;
private boolean mPowerCharged;
+ private boolean mBatteryOverheated;
+ private boolean mEnableBatteryDefender;
private int mChargingSpeed;
private int mChargingWattage;
private int mBatteryLevel;
@@ -410,7 +412,7 @@ public class KeyguardIndicationController implements StateListener,
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
- } else if (mPowerPluggedIn) {
+ } else if (mPowerPluggedIn || mEnableBatteryDefender) {
String indication = computePowerIndication();
if (animate) {
animateText(mTextView, indication);
@@ -430,7 +432,7 @@ public class KeyguardIndicationController implements StateListener,
String trustManagedIndication = getTrustManagedIndication();
String powerIndication = null;
- if (mPowerPluggedIn) {
+ if (mPowerPluggedIn || mEnableBatteryDefender) {
powerIndication = computePowerIndication();
}
@@ -465,7 +467,7 @@ public class KeyguardIndicationController implements StateListener,
mTextView.switchIndication(mAlignmentIndication);
isError = true;
hideIndication = !mBatteryPresent;
- } else if (mPowerPluggedIn) {
+ } else if (mPowerPluggedIn || mEnableBatteryDefender) {
if (DEBUG_CHARGING_SPEED) {
powerIndication += ", " + (mChargingWattage / 1000) + " mW";
}
@@ -545,8 +547,14 @@ public class KeyguardIndicationController implements StateListener,
return mContext.getResources().getString(R.string.keyguard_charged);
}
- final boolean hasChargingTime = mChargingTimeRemaining > 0;
int chargingId;
+ String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f);
+ if (mBatteryOverheated) {
+ chargingId = R.string.keyguard_plugged_in_charging_limited;
+ return mContext.getResources().getString(chargingId, percentage);
+ }
+
+ final boolean hasChargingTime = mChargingTimeRemaining > 0;
if (mPowerPluggedInWired) {
switch (mChargingSpeed) {
case BatteryStatus.CHARGING_FAST:
@@ -571,8 +579,6 @@ public class KeyguardIndicationController implements StateListener,
: R.string.keyguard_plugged_in_wireless;
}
- String percentage = NumberFormat.getPercentInstance()
- .format(mBatteryLevel / 100f);
if (hasChargingTime) {
String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, mChargingTimeRemaining);
@@ -692,6 +698,8 @@ public class KeyguardIndicationController implements StateListener,
mChargingSpeed = status.getChargingSpeed(mContext);
mBatteryLevel = status.level;
mBatteryPresent = status.present;
+ mBatteryOverheated = status.isOverheated();
+ mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn();
try {
mChargingTimeRemaining = mPowerPluggedIn
? mBatteryInfo.computeChargeTimeRemaining() : -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 3c4830272099..7eb921b9e6bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -335,7 +335,7 @@ public class NotificationLockscreenUserManagerImpl implements
}
NotificationEntry visibleEntry = getEntryManager().getActiveNotificationUnfiltered(key);
return isLockscreenPublicMode(mCurrentUserId) && visibleEntry != null
- && visibleEntry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET;
+ && visibleEntry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET;
}
public boolean shouldShowOnKeyguard(NotificationEntry entry) {
@@ -513,7 +513,8 @@ public class NotificationLockscreenUserManagerImpl implements
}
NotificationEntry entry = getEntryManager().getActiveNotificationUnfiltered(key);
return entry != null
- && entry.getRanking().getVisibilityOverride() == Notification.VISIBILITY_PRIVATE;
+ && entry.getRanking().getLockscreenVisibilityOverride()
+ == Notification.VISIBILITY_PRIVATE;
}
private void updateCurrentProfilesCache() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index b1c6f535ba87..23d5369833c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -120,7 +120,7 @@ public class KeyguardCoordinator implements Coordinator {
// notifications to show in public mode
if (mLockscreenUserManager.isLockscreenPublicMode(currUserId)
|| mLockscreenUserManager.isLockscreenPublicMode(notifUserId)) {
- if (entry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET) {
+ if (entry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 37d5da24a704..7c5d4a3efee7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.notification.TransformState.TRANSFO
import android.app.Notification;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
import android.view.NotificationTopLineView;
@@ -168,9 +169,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
public void applyConversationSkin() {
if (mAppNameText != null) {
+ final ColorStateList colors = mAppNameText.getTextColors();
mAppNameText.setTextAppearance(
com.android.internal.R.style
.TextAppearance_DeviceDefault_Notification_Conversation_AppName);
+ mAppNameText.setTextColor(colors);
MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
layoutParams.setMarginStart(0);
}
@@ -189,11 +192,13 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
public void clearConversationSkin() {
if (mAppNameText != null) {
+ final ColorStateList colors = mAppNameText.getTextColors();
final int textAppearance = Utils.getThemeAttr(
mAppNameText.getContext(),
com.android.internal.R.attr.notificationHeaderTextAppearance,
com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info);
mAppNameText.setTextAppearance(textAppearance);
+ mAppNameText.setTextColor(colors);
MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
final int marginStart = mAppNameText.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_header_app_name_margin_start);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ac3b6d26fab6..885048df13f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -82,6 +82,9 @@ public class AmbientState {
private ExpandableNotificationRow mTrackedHeadsUpRow;
private float mAppearFraction;
+ /** Tracks the state from AlertingNotificationManager#hasNotifications() */
+ private boolean mHasAlertEntries;
+
public AmbientState(
Context context,
@NonNull SectionProvider sectionProvider) {
@@ -365,10 +368,21 @@ public class AmbientState {
mPanelTracking = panelTracking;
}
+ public boolean hasPulsingNotifications() {
+ return mPulsing && mHasAlertEntries;
+ }
+
public void setPulsing(boolean hasPulsing) {
mPulsing = hasPulsing;
}
+ /**
+ * @return if we're pulsing in general
+ */
+ public boolean isPulsing() {
+ return mPulsing;
+ }
+
public boolean isPulsing(NotificationEntry entry) {
return mPulsing && entry.isAlerting();
}
@@ -527,4 +541,8 @@ public class AmbientState {
public float getAppearFraction() {
return mAppearFraction;
}
+
+ public void setHasAlertEntries(boolean hasAlertEntries) {
+ mHasAlertEntries = hasAlertEntries;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
index ba03a50b0ba5..1131a65abe93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
@@ -31,22 +31,175 @@ import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableView;
/**
- * Represents the priority of a notification section and tracks first and last visible children.
+ * Represents the bounds of a section of the notification shade and handles animation when the
+ * bounds change.
*/
public class NotificationSection {
private @PriorityBucket int mBucket;
+ private View mOwningView;
+ private Rect mBounds = new Rect();
+ private Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
+ private Rect mStartAnimationRect = new Rect();
+ private Rect mEndAnimationRect = new Rect();
+ private ObjectAnimator mTopAnimator = null;
+ private ObjectAnimator mBottomAnimator = null;
private ExpandableView mFirstVisibleChild;
private ExpandableView mLastVisibleChild;
- NotificationSection(@PriorityBucket int bucket) {
+ NotificationSection(View owningView, @PriorityBucket int bucket) {
+ mOwningView = owningView;
mBucket = bucket;
}
+ public void cancelAnimators() {
+ if (mBottomAnimator != null) {
+ mBottomAnimator.cancel();
+ }
+ if (mTopAnimator != null) {
+ mTopAnimator.cancel();
+ }
+ }
+
+ public Rect getCurrentBounds() {
+ return mCurrentBounds;
+ }
+
+ public Rect getBounds() {
+ return mBounds;
+ }
+
+ public boolean didBoundsChange() {
+ return !mCurrentBounds.equals(mBounds);
+ }
+
+ public boolean areBoundsAnimating() {
+ return mBottomAnimator != null || mTopAnimator != null;
+ }
+
@PriorityBucket
public int getBucket() {
return mBucket;
}
+ public void startBackgroundAnimation(boolean animateTop, boolean animateBottom) {
+ // Left and right bounds are always applied immediately.
+ mCurrentBounds.left = mBounds.left;
+ mCurrentBounds.right = mBounds.right;
+ startBottomAnimation(animateBottom);
+ startTopAnimation(animateTop);
+ }
+
+
+ @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.STATE_RESOLVER)
+ private void startTopAnimation(boolean animate) {
+ int previousEndValue = mEndAnimationRect.top;
+ int newEndValue = mBounds.top;
+ ObjectAnimator previousAnimator = mTopAnimator;
+ if (previousAnimator != null && previousEndValue == newEndValue) {
+ return;
+ }
+ if (!animate) {
+ // just a local update was performed
+ if (previousAnimator != null) {
+ // we need to increase all animation keyframes of the previous animator by the
+ // relative change to the end value
+ int previousStartValue = mStartAnimationRect.top;
+ PropertyValuesHolder[] values = previousAnimator.getValues();
+ values[0].setIntValues(previousStartValue, newEndValue);
+ mStartAnimationRect.top = previousStartValue;
+ mEndAnimationRect.top = newEndValue;
+ previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+ return;
+ } else {
+ // no new animation needed, let's just apply the value
+ setBackgroundTop(newEndValue);
+ return;
+ }
+ }
+ if (previousAnimator != null) {
+ previousAnimator.cancel();
+ }
+ ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
+ mCurrentBounds.top, newEndValue);
+ Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+ animator.setInterpolator(interpolator);
+ animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ // remove the tag when the animation is finished
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStartAnimationRect.top = -1;
+ mEndAnimationRect.top = -1;
+ mTopAnimator = null;
+ }
+ });
+ animator.start();
+ mStartAnimationRect.top = mCurrentBounds.top;
+ mEndAnimationRect.top = newEndValue;
+ mTopAnimator = animator;
+ }
+
+ @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.STATE_RESOLVER)
+ private void startBottomAnimation(boolean animate) {
+ int previousStartValue = mStartAnimationRect.bottom;
+ int previousEndValue = mEndAnimationRect.bottom;
+ int newEndValue = mBounds.bottom;
+ ObjectAnimator previousAnimator = mBottomAnimator;
+ if (previousAnimator != null && previousEndValue == newEndValue) {
+ return;
+ }
+ if (!animate) {
+ // just a local update was performed
+ if (previousAnimator != null) {
+ // we need to increase all animation keyframes of the previous animator by the
+ // relative change to the end value
+ PropertyValuesHolder[] values = previousAnimator.getValues();
+ values[0].setIntValues(previousStartValue, newEndValue);
+ mStartAnimationRect.bottom = previousStartValue;
+ mEndAnimationRect.bottom = newEndValue;
+ previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+ return;
+ } else {
+ // no new animation needed, let's just apply the value
+ setBackgroundBottom(newEndValue);
+ return;
+ }
+ }
+ if (previousAnimator != null) {
+ previousAnimator.cancel();
+ }
+ ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
+ mCurrentBounds.bottom, newEndValue);
+ Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
+ animator.setInterpolator(interpolator);
+ animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ // remove the tag when the animation is finished
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStartAnimationRect.bottom = -1;
+ mEndAnimationRect.bottom = -1;
+ mBottomAnimator = null;
+ }
+ });
+ animator.start();
+ mStartAnimationRect.bottom = mCurrentBounds.bottom;
+ mEndAnimationRect.bottom = newEndValue;
+ mBottomAnimator = animator;
+ }
+
+ @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.SHADE_VIEW)
+ private void setBackgroundTop(int top) {
+ mCurrentBounds.top = top;
+ mOwningView.invalidate();
+ }
+
+ @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.SHADE_VIEW)
+ private void setBackgroundBottom(int bottom) {
+ mCurrentBounds.bottom = bottom;
+ mOwningView.invalidate();
+ }
+
public ExpandableView getFirstVisibleChild() {
return mFirstVisibleChild;
}
@@ -66,4 +219,93 @@ public class NotificationSection {
mLastVisibleChild = child;
return changed;
}
+
+ public void resetCurrentBounds() {
+ mCurrentBounds.set(mBounds);
+ }
+
+ /**
+ * Returns true if {@code top} is equal to the top of this section (if not currently animating)
+ * or where the top of this section will be when animation completes.
+ */
+ public boolean isTargetTop(int top) {
+ return (mTopAnimator == null && mCurrentBounds.top == top)
+ || (mTopAnimator != null && mEndAnimationRect.top == top);
+ }
+
+ /**
+ * Returns true if {@code bottom} is equal to the bottom of this section (if not currently
+ * animating) or where the bottom of this section will be when animation completes.
+ */
+ public boolean isTargetBottom(int bottom) {
+ return (mBottomAnimator == null && mCurrentBounds.bottom == bottom)
+ || (mBottomAnimator != null && mEndAnimationRect.bottom == bottom);
+ }
+
+ /**
+ * Update the bounds of this section based on it's views
+ *
+ * @param minTopPosition the minimum position that the top needs to have
+ * @param minBottomPosition the minimum position that the bottom needs to have
+ * @return the position of the new bottom
+ */
+ public int updateBounds(int minTopPosition, int minBottomPosition,
+ boolean shiftBackgroundWithFirst) {
+ int top = minTopPosition;
+ int bottom = minTopPosition;
+ ExpandableView firstView = getFirstVisibleChild();
+ if (firstView != null) {
+ // Round Y up to avoid seeing the background during animation
+ int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
+ // TODO: look into the already animating part
+ int newTop;
+ if (isTargetTop(finalTranslationY)) {
+ // we're ending up at the same location as we are now, let's just skip the
+ // animation
+ newTop = finalTranslationY;
+ } else {
+ newTop = (int) Math.ceil(firstView.getTranslationY());
+ }
+ top = Math.max(newTop, top);
+ if (firstView.showingPulsing()) {
+ // If we're pulsing, the notification can actually go below!
+ bottom = Math.max(bottom, finalTranslationY
+ + ExpandableViewState.getFinalActualHeight(firstView));
+ if (shiftBackgroundWithFirst) {
+ mBounds.left += Math.max(firstView.getTranslation(), 0);
+ mBounds.right += Math.min(firstView.getTranslation(), 0);
+ }
+ }
+ }
+ top = Math.max(minTopPosition, top);
+ ExpandableView lastView = getLastVisibleChild();
+ if (lastView != null) {
+ float finalTranslationY = ViewState.getFinalTranslationY(lastView);
+ int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
+ // Round Y down to avoid seeing the background during animation
+ int finalBottom = (int) Math.floor(
+ finalTranslationY + finalHeight - lastView.getClipBottomAmount());
+ int newBottom;
+ if (isTargetBottom(finalBottom)) {
+ // we're ending up at the same location as we are now, lets just skip the animation
+ newBottom = finalBottom;
+ } else {
+ newBottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
+ - lastView.getClipBottomAmount());
+ // The background can never be lower than the end of the last view
+ minBottomPosition = (int) Math.min(
+ lastView.getTranslationY() + lastView.getActualHeight(),
+ minBottomPosition);
+ }
+ bottom = Math.max(bottom, Math.max(newBottom, minBottomPosition));
+ }
+ bottom = Math.max(top, bottom);
+ mBounds.top = top;
+ mBounds.bottom = bottom;
+ return bottom;
+ }
+
+ public boolean needsBackground() {
+ return mFirstVisibleChild != null && mBucket != BUCKET_MEDIA_CONTROLS;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 36c5419b7399..4f7e14ba4a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -126,7 +126,7 @@ class NotificationSectionsManager @Inject internal constructor(
fun createSectionsForBuckets(): Array<NotificationSection> =
sectionsFeatureManager.getNotificationBuckets()
- .map { NotificationSection(it) }
+ .map { NotificationSection(parent, it) }
.toTypedArray()
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 5cc17a08504f..4487142e8907 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -38,9 +38,12 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PointF;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
@@ -69,6 +72,7 @@ import android.widget.OverScroller;
import android.widget.ScrollView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
@@ -153,6 +157,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private ExpandHelper mExpandHelper;
private NotificationSwipeHelper mSwipeHelper;
private int mCurrentStackHeight = Integer.MAX_VALUE;
+ private final Paint mBackgroundPaint = new Paint();
+ private final boolean mShouldDrawNotificationBackground;
private boolean mHighPriorityBeforeSpeedBump;
private boolean mDismissRtl;
@@ -251,6 +257,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
private boolean mDismissAllInProgress;
+ private boolean mFadeNotificationsOnDismiss;
private FooterDismissListener mFooterDismissListener;
private boolean mFlingAfterUpEvent;
@@ -320,6 +327,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
private NotificationSection[] mSections;
+ private boolean mAnimateNextBackgroundTop;
+ private boolean mAnimateNextBackgroundBottom;
+ private boolean mAnimateNextSectionBoundsChange;
+ private int mBgColor;
private float mDimAmount;
private ValueAnimator mDimAnimator;
private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
@@ -330,14 +341,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
private ValueAnimator.AnimatorUpdateListener mDimUpdateListener
- = animation -> setDimAmount((Float) animation.getAnimatedValue());
+ = new ValueAnimator.AnimatorUpdateListener() {
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setDimAmount((Float) animation.getAnimatedValue());
+ }
+ };
protected ViewGroup mQsContainer;
private boolean mContinuousShadowUpdate;
+ private boolean mContinuousBackgroundUpdate;
private ViewTreeObserver.OnPreDrawListener mShadowUpdater
= () -> {
updateViewShadows();
return true;
};
+ private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
+ updateBackground();
+ return true;
+ };
private Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> {
float endY = view.getTranslationY() + view.getActualHeight();
float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
@@ -356,7 +378,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mAmbientState.isHiddenAtAll()) {
float xProgress = mHideXInterpolator.getInterpolation(
(1 - mLinearHideAmount) * mBackgroundXFactor);
- outline.setRoundRect(mOutlineAnimationRect,
+ outline.setRoundRect(mBackgroundAnimationRect,
MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius,
xProgress));
outline.setAlpha(1.0f - mAmbientState.getHideAmount());
@@ -365,6 +387,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
};
+ private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
private boolean mPulsing;
private boolean mScrollable;
private View mForcedScroll;
@@ -397,6 +420,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mInHeadsUpPinnedMode;
private boolean mHeadsUpAnimatingAway;
private int mStatusBarState;
+ private int mCachedBackgroundColor;
private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
private Runnable mReflingAndAnimateScroll = () -> {
if (ANCHOR_SCROLLING) {
@@ -406,7 +430,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
};
private int mCornerRadius;
private int mSidePaddings;
- private final Rect mOutlineAnimationRect = new Rect();
+ private final Rect mBackgroundAnimationRect = new Rect();
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
@@ -426,6 +450,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final NotificationSectionsManager mSectionsManager;
private ForegroundServiceDungeonView mFgsSectionView;
+ private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
private float mLastSentExpandedHeight;
private boolean mWillExpand;
@@ -436,6 +461,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mKeyguardMediaControllorVisible;
private NotificationEntry mTopHeadsUpEntry;
+ private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
@@ -502,6 +528,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSections = mSectionsManager.createSectionsForBuckets();
mAmbientState = new AmbientState(context, mSectionsManager);
+ mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor();
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
@@ -510,9 +537,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mExpandHelper.setScrollAdapter(mScrollAdapter);
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
+ mShouldDrawNotificationBackground =
+ res.getBoolean(R.bool.config_drawNotificationBackground);
setOutlineProvider(mOutlineProvider);
- setWillNotDraw(!DEBUG);
+ boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
+ setWillNotDraw(!willDraw);
+ mBackgroundPaint.setAntiAlias(true);
if (DEBUG) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0xffff0000);
@@ -557,7 +588,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* @return the height at which we will wake up when pulsing
*/
public float getWakeUpHeight() {
- ExpandableView firstChild = getFirstExpandableView();
+ ExpandableView firstChild = getFirstChildWithBackground();
if (firstChild != null) {
if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
return firstChild.getHeadsUpHeightWithoutHeader();
@@ -608,11 +639,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
void updateBgColor() {
+ mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor();
+ updateBackgroundDimming();
mShelf.onUiModeChanged();
}
@ShadeViewRefactor(RefactorComponent.DECORATOR)
protected void onDraw(Canvas canvas) {
+ if (mShouldDrawNotificationBackground
+ && (mSections[0].getCurrentBounds().top
+ < mSections[mSections.length - 1].getCurrentBounds().bottom
+ || mAmbientState.isDozing())) {
+ drawBackground(canvas);
+ } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) {
+ drawHeadsUpBackground(canvas);
+ }
+
if (DEBUG) {
int y = mTopPadding;
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -647,6 +689,160 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
+ @ShadeViewRefactor(RefactorComponent.DECORATOR)
+ private void drawBackground(Canvas canvas) {
+ int lockScreenLeft = mSidePaddings;
+ int lockScreenRight = getWidth() - mSidePaddings;
+ int lockScreenTop = mSections[0].getCurrentBounds().top;
+ int lockScreenBottom = mSections[mSections.length - 1].getCurrentBounds().bottom;
+ int hiddenLeft = getWidth() / 2;
+ int hiddenTop = mTopPadding;
+
+ float yProgress = 1 - mInterpolatedHideAmount;
+ float xProgress = mHideXInterpolator.getInterpolation(
+ (1 - mLinearHideAmount) * mBackgroundXFactor);
+
+ int left = (int) MathUtils.lerp(hiddenLeft, lockScreenLeft, xProgress);
+ int right = (int) MathUtils.lerp(hiddenLeft, lockScreenRight, xProgress);
+ int top = (int) MathUtils.lerp(hiddenTop, lockScreenTop, yProgress);
+ int bottom = (int) MathUtils.lerp(hiddenTop, lockScreenBottom, yProgress);
+ mBackgroundAnimationRect.set(
+ left,
+ top,
+ right,
+ bottom);
+
+ int backgroundTopAnimationOffset = top - lockScreenTop;
+ // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD
+ boolean anySectionHasVisibleChild = false;
+ for (NotificationSection section : mSections) {
+ if (section.needsBackground()) {
+ anySectionHasVisibleChild = true;
+ break;
+ }
+ }
+ boolean shouldDrawBackground;
+ if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
+ shouldDrawBackground = isPulseExpanding();
+ } else {
+ shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild;
+ }
+ if (shouldDrawBackground) {
+ drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset);
+ }
+
+ updateClipping();
+ }
+
+ /**
+ * Draws round rects for each background section.
+ *
+ * We want to draw a round rect for each background section as defined by {@link #mSections}.
+ * However, if two sections are directly adjacent with no gap between them (e.g. on the
+ * lockscreen where the shelf can appear directly below the high priority section, or while
+ * scrolling the shade so that the top of the shelf is right at the bottom of the high priority
+ * section), we don't want to round the adjacent corners.
+ *
+ * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we
+ * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect.
+ * This method tracks the top of each rect we need to draw, then iterates through the visible
+ * sections. If a section is not adjacent to the previous section, we draw the previous rect
+ * behind the sections we've accumulated up to that point, then start a new rect at the top of
+ * the current section. When we're done iterating we will always have one rect left to draw.
+ */
+ private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
+ int animationYOffset) {
+ int backgroundRectTop = top;
+ int lastSectionBottom =
+ mSections[0].getCurrentBounds().bottom + animationYOffset;
+ int currentLeft = left;
+ int currentRight = right;
+ boolean first = true;
+ for (NotificationSection section : mSections) {
+ if (!section.needsBackground()) {
+ continue;
+ }
+ int sectionTop = section.getCurrentBounds().top + animationYOffset;
+ int ownLeft = Math.min(Math.max(left, section.getCurrentBounds().left), right);
+ int ownRight = Math.max(Math.min(right, section.getCurrentBounds().right), ownLeft);
+ // If sections are directly adjacent to each other, we don't want to draw them
+ // as separate roundrects, as the rounded corners right next to each other look
+ // bad.
+ if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX
+ || ((currentLeft != ownLeft || currentRight != ownRight) && !first)) {
+ canvas.drawRoundRect(currentLeft,
+ backgroundRectTop,
+ currentRight,
+ lastSectionBottom,
+ mCornerRadius, mCornerRadius, mBackgroundPaint);
+ backgroundRectTop = sectionTop;
+ }
+ currentLeft = ownLeft;
+ currentRight = ownRight;
+ lastSectionBottom =
+ section.getCurrentBounds().bottom + animationYOffset;
+ first = false;
+ }
+ canvas.drawRoundRect(currentLeft,
+ backgroundRectTop,
+ currentRight,
+ lastSectionBottom,
+ mCornerRadius, mCornerRadius, mBackgroundPaint);
+ }
+
+ private void drawHeadsUpBackground(Canvas canvas) {
+ int left = mSidePaddings;
+ int right = getWidth() - mSidePaddings;
+
+ float top = getHeight();
+ float bottom = 0;
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE
+ && child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0
+ && row.getProvider().shouldShowGutsOnSnapOpen()) {
+ top = Math.min(top, row.getTranslationY());
+ bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight());
+ }
+ }
+ }
+
+ if (top < bottom) {
+ canvas.drawRoundRect(
+ left, top, right, bottom,
+ mCornerRadius, mCornerRadius, mBackgroundPaint);
+ }
+ }
+
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ void updateBackgroundDimming() {
+ // No need to update the background color if it's not being drawn.
+ if (!mShouldDrawNotificationBackground) {
+ return;
+ }
+ final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1;
+ if (newFlowHideShelf) {
+ mBackgroundPaint.setColor(Color.TRANSPARENT);
+ invalidate();
+ return;
+ }
+ // Interpolate between semi-transparent notification panel background color
+ // and white AOD separator.
+ float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */,
+ mLinearHideAmount);
+ int color = ColorUtils.blendARGB(mBgColor, Color.WHITE, colorInterpolation);
+
+ if (mCachedBackgroundColor != color) {
+ mCachedBackgroundColor = color;
+ mBackgroundPaint.setColor(color);
+ invalidate();
+ }
+ }
+
private void reinitView() {
initView(getContext(), mKeyguardBypassEnabledProvider, mSwipeHelper);
}
@@ -781,7 +977,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateContentHeight();
clampScrollPosition();
requestChildrenUpdate();
- updateFirstAndLastExpandableView();
+ updateFirstAndLastBackgroundViews();
updateAlgorithmLayoutMinHeight();
updateOwnTranslationZ();
}
@@ -852,6 +1048,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void onPreDrawDuringAnimation() {
mShelf.updateAppearance();
updateClippingToTopRoundedCorner();
+ if (!mNeedsAnimation && !mChildrenUpdateRequested) {
+ updateBackground();
+ }
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -2168,6 +2367,131 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ private void updateBackground() {
+ // No need to update the background color if it's not being drawn.
+ if (!mShouldDrawNotificationBackground) {
+ return;
+ }
+
+ updateBackgroundBounds();
+ if (didSectionBoundsChange()) {
+ boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop
+ || mAnimateNextBackgroundBottom || areSectionBoundsAnimating();
+ if (!isExpanded()) {
+ abortBackgroundAnimators();
+ animate = false;
+ }
+ if (animate) {
+ startBackgroundAnimation();
+ } else {
+ for (NotificationSection section : mSections) {
+ section.resetCurrentBounds();
+ }
+ invalidate();
+ }
+ } else {
+ abortBackgroundAnimators();
+ }
+ mAnimateNextBackgroundTop = false;
+ mAnimateNextBackgroundBottom = false;
+ mAnimateNextSectionBoundsChange = false;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+ private void abortBackgroundAnimators() {
+ for (NotificationSection section : mSections) {
+ section.cancelAnimators();
+ }
+ }
+
+ private boolean didSectionBoundsChange() {
+ for (NotificationSection section : mSections) {
+ if (section.didBoundsChange()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+ private boolean areSectionBoundsAnimating() {
+ for (NotificationSection section : mSections) {
+ if (section.areBoundsAnimating()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+ private void startBackgroundAnimation() {
+ // TODO(kprevas): do we still need separate fields for top/bottom?
+ // or can each section manage its own animation state?
+ NotificationSection firstVisibleSection = getFirstVisibleSection();
+ NotificationSection lastVisibleSection = getLastVisibleSection();
+ for (NotificationSection section : mSections) {
+ section.startBackgroundAnimation(
+ section == firstVisibleSection
+ ? mAnimateNextBackgroundTop
+ : mAnimateNextSectionBoundsChange,
+ section == lastVisibleSection
+ ? mAnimateNextBackgroundBottom
+ : mAnimateNextSectionBoundsChange);
+ }
+ }
+
+ /**
+ * Update the background bounds to the new desired bounds
+ */
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ private void updateBackgroundBounds() {
+ int left = mSidePaddings;
+ int right = getWidth() - mSidePaddings;
+ for (NotificationSection section : mSections) {
+ section.getBounds().left = left;
+ section.getBounds().right = right;
+ }
+
+ if (!mIsExpanded) {
+ for (NotificationSection section : mSections) {
+ section.getBounds().top = 0;
+ section.getBounds().bottom = 0;
+ }
+ return;
+ }
+ int minTopPosition;
+ NotificationSection lastSection = getLastVisibleSection();
+ boolean onKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
+ if (!onKeyguard) {
+ minTopPosition = (int) (mTopPadding + mStackTranslation);
+ } else if (lastSection == null) {
+ minTopPosition = mTopPadding;
+ } else {
+ // The first sections could be empty while there could still be elements in later
+ // sections. The position of these first few sections is determined by the position of
+ // the first visible section.
+ NotificationSection firstVisibleSection = getFirstVisibleSection();
+ firstVisibleSection.updateBounds(0 /* minTopPosition*/, 0 /* minBottomPosition */,
+ false /* shiftPulsingWithFirst */);
+ minTopPosition = firstVisibleSection.getBounds().top;
+ }
+ boolean shiftPulsingWithFirst = mNumHeadsUp <= 1
+ && (mAmbientState.isDozing()
+ || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
+ for (NotificationSection section : mSections) {
+ int minBottomPosition = minTopPosition;
+ if (section == lastSection) {
+ // We need to make sure the section goes all the way to the shelf
+ minBottomPosition = (int) (ViewState.getFinalTranslationY(mShelf)
+ + mShelf.getIntrinsicHeight());
+ }
+ minTopPosition = section.updateBounds(minTopPosition, minBottomPosition,
+ shiftPulsingWithFirst);
+ shiftPulsingWithFirst = false;
+ }
+ }
+
private NotificationSection getFirstVisibleSection() {
for (NotificationSection section : mSections) {
if (section.getFirstVisibleChild() != null) {
@@ -2188,7 +2512,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private ExpandableView getLastExpandableView() {
+ private ExpandableView getLastChildWithBackground() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
ExpandableView child = (ExpandableView) getChildAt(i);
@@ -2201,7 +2525,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- private ExpandableView getFirstExpandableView() {
+ private ExpandableView getFirstChildWithBackground() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
@@ -2214,7 +2538,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
//TODO: We shouldn't have to generate this list every time
- private List<ExpandableView> getExpandableViewList() {
+ private List<ExpandableView> getChildrenWithBackground() {
ArrayList<ExpandableView> children = new ArrayList<>();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -2752,13 +3076,32 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- private void updateFirstAndLastExpandableView() {
- ExpandableView lastChild = getLastExpandableView();
- mSectionsManager.updateFirstAndLastViewsForAllSections(
- mSections, getExpandableViewList());
+ private void updateFirstAndLastBackgroundViews() {
+ NotificationSection firstSection = getFirstVisibleSection();
+ NotificationSection lastSection = getLastVisibleSection();
+ ExpandableView previousFirstChild =
+ firstSection == null ? null : firstSection.getFirstVisibleChild();
+ ExpandableView previousLastChild =
+ lastSection == null ? null : lastSection.getLastVisibleChild();
+
+ ExpandableView firstChild = getFirstChildWithBackground();
+ ExpandableView lastChild = getLastChildWithBackground();
+ boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections(
+ mSections, getChildrenWithBackground());
+
+ if (mAnimationsEnabled && mIsExpanded) {
+ mAnimateNextBackgroundTop = firstChild != previousFirstChild;
+ mAnimateNextBackgroundBottom = lastChild != previousLastChild || mAnimateBottomOnLayout;
+ mAnimateNextSectionBoundsChange = sectionViewsChanged;
+ } else {
+ mAnimateNextBackgroundTop = false;
+ mAnimateNextBackgroundBottom = false;
+ mAnimateNextSectionBoundsChange = false;
+ }
mAmbientState.setLastVisibleBackgroundChild(lastChild);
// TODO: Refactor SectionManager and put the RoundnessManager there.
mController.getNoticationRoundessManager().updateRoundedChildren(mSections);
+ mAnimateBottomOnLayout = false;
invalidate();
}
@@ -2920,6 +3263,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
setAnimationRunning(true);
mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
mAnimationEvents.clear();
+ updateBackground();
updateViewShadows();
updateClippingToTopRoundedCorner();
} else {
@@ -4004,6 +4348,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void setDimAmount(float dimAmount) {
mDimAmount = dimAmount;
+ updateBackgroundDimming();
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4072,6 +4417,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
runAnimationFinishedRunnables();
setAnimationRunning(false);
+ updateBackground();
updateViewShadows();
updateClippingToTopRoundedCorner();
}
@@ -4200,6 +4546,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
invalidateOutline();
}
updateAlgorithmHeightAndPadding();
+ updateBackgroundDimming();
requestChildrenUpdate();
updateOwnTranslationZ();
}
@@ -5080,6 +5427,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
public void setDozeAmount(float dozeAmount) {
mAmbientState.setDozeAmount(dozeAmount);
+ updateContinuousBackgroundDrawing();
requestChildrenUpdate();
}
@@ -5117,6 +5465,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
void setAnimateBottomOnLayout(boolean animateBottomOnLayout) {
+ mAnimateBottomOnLayout = animateBottomOnLayout;
}
public void setOnPulseHeightChangedListener(Runnable listener) {
@@ -5149,14 +5498,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
void onSwipeBegin() {
requestDisallowInterceptTouchEvent(true);
+ updateFirstAndLastBackgroundViews();
updateContinuousShadowDrawing();
+ updateContinuousBackgroundDrawing();
requestChildrenUpdate();
}
+ void onSwipeEnd() {
+ updateFirstAndLastBackgroundViews();
+ }
+
void setTopHeadsUpEntry(NotificationEntry topEntry) {
mTopHeadsUpEntry = topEntry;
}
+ void setNumHeadsUp(long numHeadsUp) {
+ mNumHeadsUp = numHeadsUp;
+ mAmbientState.setHasAlertEntries(numHeadsUp > 0);
+ }
+
public boolean getIsExpanded() {
return mIsExpanded;
}
@@ -5271,6 +5631,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSectionsManager.updateSectionBoundaries(reason);
}
+ boolean isSilkDismissEnabled() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* enabled by default */) == 1;
+ }
+
+ void updateContinuousBackgroundDrawing() {
+ if (isSilkDismissEnabled()) {
+ return;
+ }
+ boolean continuousBackground = !mAmbientState.isFullyAwake()
+ && mSwipeHelper.isSwiping();
+ if (continuousBackground != mContinuousBackgroundUpdate) {
+ mContinuousBackgroundUpdate = continuousBackground;
+ if (continuousBackground) {
+ getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater);
+ } else {
+ getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater);
+ }
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
void updateContinuousShadowDrawing() {
boolean continuousShadowUpdate = mAnimationRunning
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 8a0330912502..c2d030b132db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -395,6 +395,7 @@ public class NotificationStackScrollLayoutController {
if (mView.getDismissAllInProgress()) {
return;
}
+ mView.onSwipeEnd();
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isHeadsUp()) {
@@ -454,6 +455,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void onChildSnappedBack(View animView, float targetLeft) {
+ mView.onSwipeEnd();
if (animView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
if (row.isPinned() && !canChildBeDismissed(row)
@@ -517,7 +519,9 @@ public class NotificationStackScrollLayoutController {
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ long numEntries = mHeadsUpManager.getAllEntries().count();
NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
+ mView.setNumHeadsUp(numEntries);
mView.setTopHeadsUpEntry(topEntry);
mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */);
}
@@ -661,6 +665,8 @@ public class NotificationStackScrollLayoutController {
mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
+ mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
+
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
mFadeNotificationsOnDismiss = // TODO: this should probably be injected directly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 9854f5450df1..4ca9c5db013c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -289,6 +289,8 @@ public class KeyguardBouncer {
SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
mDismissCallbackRegistry.notifyDismissCancelled();
}
+ mExpansion = EXPANSION_HIDDEN;
+ dispatchExpansionChanged();
mIsScrimmed = false;
mFalsingCollector.onBouncerHidden();
mCallback.onBouncerVisiblityChanged(false /* shown */);
@@ -377,6 +379,7 @@ public class KeyguardBouncer {
*/
public void setExpansion(float fraction) {
float oldExpansion = mExpansion;
+ boolean expansionChanged = mExpansion != fraction;
mExpansion = fraction;
if (mKeyguardViewController != null && !mIsAnimatingAway) {
mKeyguardViewController.setExpansion(fraction);
@@ -394,6 +397,10 @@ public class KeyguardBouncer {
mKeyguardViewController.onStartingToHide();
}
}
+
+ if (expansionChanged) {
+ dispatchExpansionChanged();
+ }
}
public boolean willDismissWithAction() {
@@ -518,6 +525,12 @@ public class KeyguardBouncer {
}
}
+ private void dispatchExpansionChanged() {
+ for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ callback.onExpansionChanged(mExpansion);
+ }
+ }
+
public void dump(PrintWriter pw) {
pw.println("KeyguardBouncer");
pw.println(" isShowing(): " + isShowing());
@@ -534,6 +547,12 @@ public class KeyguardBouncer {
void onStartingToHide();
void onStartingToShow();
void onFullyHidden();
+
+ /**
+ * From 0f {@link KeyguardBouncer#EXPANSION_VISIBLE} when fully visible
+ * to 1f {@link KeyguardBouncer#EXPANSION_HIDDEN} when fully hidden
+ */
+ default void onExpansionChanged(float bouncerHideAmount) {}
}
/** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 0e7e2fd8173c..547a3705266a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED;
import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN;
import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE;
+import android.animation.ArgbEvaluator;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -38,6 +39,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -84,7 +86,7 @@ public class LockscreenLockIconController {
private boolean mDocked;
private boolean mWakeAndUnlockRunning;
private boolean mShowingLaunchAffordance;
- private boolean mBouncerShowing;
+ private float mBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
private boolean mBouncerShowingScrimmed;
private boolean mFingerprintUnlock;
private int mStatusBarState = StatusBarState.SHADE;
@@ -104,6 +106,8 @@ public class LockscreenLockIconController {
mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
mConfigurationListener.onThemeChanged();
+
+ updateColor();
update();
}
@@ -348,7 +352,6 @@ public class LockscreenLockIconController {
*/
public void attach(LockIcon lockIcon) {
mLockIcon = lockIcon;
- updateColor();
mLockIcon.setOnClickListener(this::handleClick);
mLockIcon.setOnLongClickListener(this::handleLongClick);
@@ -408,20 +411,44 @@ public class LockscreenLockIconController {
/** Sets whether the bouncer is showing. */
public void setBouncerShowingScrimmed(boolean showing, boolean scrimmed) {
- mBouncerShowing = showing;
mBouncerShowingScrimmed = scrimmed;
update();
}
+ /**
+ * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden
+ * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}.
+ */
+ public void setBouncerHideAmount(float hideAmount) {
+ mBouncerHiddenAmount = hideAmount;
+ updateColor();
+ }
+
private void updateColor() {
if (mLockIcon == null) {
return;
}
- TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
- null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
- int iconColor = typedArray.getColor(0, Color.WHITE);
- typedArray.recycle();
+ int iconColor = -1;
+ if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_VISIBLE) {
+ TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
+ null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
+ iconColor = typedArray.getColor(0, Color.WHITE);
+ typedArray.recycle();
+ } else if (mBouncerHiddenAmount == KeyguardBouncer.EXPANSION_HIDDEN) {
+ iconColor = Utils.getColorAttrDefaultColor(
+ mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
+ } else {
+ // bouncer is transitioning
+ TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
+ null, new int[]{ android.R.attr.textColorPrimary }, 0, 0);
+ int bouncerIconColor = typedArray.getColor(0, Color.WHITE);
+ typedArray.recycle();
+ int keyguardIconColor = Utils.getColorAttrDefaultColor(
+ mLockIcon.getContext(), com.android.systemui.R.attr.wallpaperTextColor);
+ iconColor = (int) new ArgbEvaluator().evaluate(
+ mBouncerHiddenAmount, bouncerIconColor, keyguardIconColor);
+ }
mLockIcon.updateColor(iconColor);
}
@@ -520,10 +547,7 @@ public class LockscreenLockIconController {
return changed;
}
boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked;
- boolean onKeyguardWithoutBouncer = mStatusBarState == StatusBarState.KEYGUARD
- && !mBouncerShowing;
- boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance
- || onKeyguardWithoutBouncer;
+ boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance;
boolean fingerprintOrBypass = mFingerprintUnlock
|| mKeyguardBypassController.getBypassEnabled();
if (fingerprintOrBypass && !mBouncerShowingScrimmed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 48f726225657..84eacdc41841 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -141,6 +141,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import javax.inject.Inject;
+import javax.inject.Provider;
@StatusBarComponent.StatusBarScope
public class NotificationPanelViewController extends PanelViewController {
@@ -185,7 +186,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final MetricsLogger mMetricsLogger;
private final ActivityManager mActivityManager;
private final ConfigurationController mConfigurationController;
- private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+ private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final NotificationIconAreaController mNotificationIconAreaController;
@@ -536,7 +537,7 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
ActivityManager activityManager,
ConfigurationController configurationController,
- FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
ConversationNotificationManager conversationNotificationManager,
MediaHierarchyManager mediaHierarchyManager,
@@ -551,7 +552,7 @@ public class NotificationPanelViewController extends PanelViewController {
MediaDataManager mediaDataManager) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
- latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
+ latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager);
mView = view;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
@@ -690,7 +691,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
protected void loadDimens() {
super.loadDimens();
- mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset()
+ mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get()
.setMaxLengthSeconds(0.4f).build();
mStatusBarMinHeight = mResources.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
@@ -2037,8 +2038,8 @@ public class NotificationPanelViewController extends PanelViewController {
maxHeight = calculatePanelHeightShade();
}
maxHeight = Math.max(min, maxHeight);
- if (maxHeight == 0) {
- Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): "
+ if (maxHeight == 0 || isNaN(maxHeight)) {
+ Log.wtf(TAG, "maxPanelHeight is invalid. getOverExpansionAmount(): "
+ getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: "
+ calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
+ calculatePanelHeightShade() + ", mStatusBarMinHeight = "
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 9e872ab65591..981f9a662deb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3794,6 +3794,14 @@ public class StatusBar extends SystemUI implements DemoMode,
}
/**
+ * Sets how hidden the bouncer is, where 0f is fully visible and 1f is fully hidden
+ * See {@link KeyguardBouncer#EXPANSION_VISIBLE} and {@link KeyguardBouncer#EXPANSION_HIDDEN}.
+ */
+ public void setBouncerHideAmount(float hideAmount) {
+ mLockscreenLockIconController.setBouncerHideAmount(hideAmount);
+ }
+
+ /**
* Collapses the notification shade if it is tracking or expanded.
*/
public void collapseShade() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b912614ba3e8..055b78a2c000 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -130,6 +130,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
updateStates();
updateLockIcon();
}
+
+ @Override
+ public void onExpansionChanged(float hideAmount) {
+ mStatusBar.setBouncerHideAmount(hideAmount);
+ }
};
private final DockManager.DockEventListener mDockEventListener =
new DockManager.DockEventListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index acca953629c7..6a0ebf7b5767 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -378,8 +378,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
// We have to post the removal to the UI thread for synchronization.
mMainThreadHandler.post(() -> {
final Runnable removeNotification = () -> {
- mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK);
mClickNotifier.onNotificationClick(entry.getKey(), nv);
+ mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK);
};
if (mPresenter.isCollapsing()) {
// To avoid lags we're only performing the remove
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 adbc85b8e2e5..6c5251b02291 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.policy;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Notification;
@@ -42,6 +41,7 @@ import android.text.SpannedString;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.view.ContentInfo;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -74,8 +74,8 @@ 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.Collection;
import java.util.HashMap;
-import java.util.Map;
import java.util.function.Consumer;
/**
@@ -315,8 +315,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mRemoteInputs = remoteInputs;
mRemoteInput = remoteInput;
mEditText.setHint(mRemoteInput.getLabel());
- mEditText.mSupportedMimeTypes = (remoteInput.getAllowedDataTypes() == null) ? null
- : remoteInput.getAllowedDataTypes().toArray(new String[0]);
+ mEditText.setSupportedMimeTypes(remoteInput.getAllowedDataTypes());
mEntry.editedSuggestionInfo = editedSuggestionInfo;
if (editedSuggestionInfo != null) {
@@ -570,12 +569,13 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
*/
public static class RemoteEditText extends EditText {
+ private final OnReceiveContentListener mOnReceiveContentListener = this::onReceiveContent;
+
private final Drawable mBackground;
private RemoteInputView mRemoteInputView;
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
UserHandle mUser;
- private String[] mSupportedMimeTypes;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -583,39 +583,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mLightBarController = Dependency.get(LightBarController.class);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- if (mSupportedMimeTypes != null && mSupportedMimeTypes.length > 0) {
- setOnReceiveContentListener(mSupportedMimeTypes,
- new OnReceiveContentListener() {
- @Override
- @Nullable
- public ContentInfo onReceiveContent(@NonNull View view,
- @NonNull ContentInfo payload) {
- Map<Boolean, ContentInfo> split = payload.partition(
- item -> item.getUri() != null);
- ContentInfo uriItems = split.get(true);
- ContentInfo remainingItems = split.get(false);
- if (uriItems != null) {
- ClipData clip = uriItems.getClip();
- ClipDescription description = clip.getDescription();
- if (clip.getItemCount() > 1
- || description.getMimeTypeCount() < 1
- || remainingItems != null) {
- // TODO(b/172363500): Update to loop over all the items
- return payload;
- }
- Uri contentUri = clip.getItemAt(0).getUri();
- String mimeType = description.getMimeType(0);
- Intent dataIntent = mRemoteInputView
- .prepareRemoteInputFromData(mimeType, contentUri);
- mRemoteInputView.sendRemoteInput(dataIntent);
- }
- return remainingItems;
- }
- });
+ void setSupportedMimeTypes(@Nullable Collection<String> mimeTypes) {
+ String[] types = null;
+ OnReceiveContentListener listener = null;
+ if (mimeTypes != null && !mimeTypes.isEmpty()) {
+ types = mimeTypes.toArray(new String[0]);
+ listener = mOnReceiveContentListener;
}
+ setOnReceiveContentListener(types, listener);
}
private void defocusIfNeeded(boolean animate) {
@@ -759,5 +734,28 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
setBackground(null);
}
}
+
+ private ContentInfo onReceiveContent(View view, ContentInfo payload) {
+ Pair<ContentInfo, ContentInfo> split =
+ payload.partition(item -> item.getUri() != null);
+ ContentInfo uriItems = split.first;
+ ContentInfo remainingItems = split.second;
+ if (uriItems != null) {
+ ClipData clip = uriItems.getClip();
+ ClipDescription description = clip.getDescription();
+ if (clip.getItemCount() > 1
+ || description.getMimeTypeCount() < 1
+ || remainingItems != null) {
+ // TODO(b/172363500): Update to loop over all the items
+ return payload;
+ }
+ Uri contentUri = clip.getItemAt(0).getUri();
+ String mimeType = description.getMimeType(0);
+ Intent dataIntent =
+ mRemoteInputView.prepareRemoteInputFromData(mimeType, contentUri);
+ mRemoteInputView.sendRemoteInput(dataIntent);
+ }
+ return remainingItems;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
index 286b7c049fc7..21d700e41a40 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
@@ -35,6 +35,8 @@ import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
@@ -58,6 +60,9 @@ public class UsbConfirmActivity extends AlertActivity
@Override
public void onCreate(Bundle icicle) {
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
super.onCreate(icicle);
Intent intent = getIntent();
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 86ba5f1f74a9..8dea5b5a19a3 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -22,6 +22,7 @@ import com.android.systemui.dagger.WMSingleton;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
@@ -34,6 +35,7 @@ import com.android.wm.shell.pip.tv.PipController;
import com.android.wm.shell.pip.tv.PipControlsView;
import com.android.wm.shell.pip.tv.PipControlsViewController;
import com.android.wm.shell.pip.tv.PipNotification;
+import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
@@ -44,7 +46,7 @@ import dagger.Provides;
/**
* Dagger module for TV Pip.
*/
-@Module
+@Module(includes = {WMShellBaseModule.class})
public abstract class TvPipModule {
@WMSingleton
@Provides
@@ -53,6 +55,7 @@ public abstract class TvPipModule {
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
+ TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
PipNotification pipNotification,
TaskStackListenerImpl taskStackListener,
@@ -63,6 +66,7 @@ public abstract class TvPipModule {
pipBoundsState,
pipBoundsAlgorithm,
pipTaskOrganizer,
+ tvPipMenuController,
pipMediaController,
pipNotification,
taskStackListener,
@@ -104,14 +108,22 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
+ static TvPipMenuController providesPipTvMenuController(Context context,
+ PipBoundsState pipBoundsState, SystemWindows systemWindows) {
+ return new TvPipMenuController(context, pipBoundsState, systemWindows);
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
+ TvPipMenuController tvMenuController,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
- null /* menuActivityController */, pipSurfaceTransactionHelper, splitScreenOptional,
+ tvMenuController, pipSurfaceTransactionHelper, splitScreenOptional,
displayController, pipUiEventLogger, shellTaskOrganizer);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index f88bedd88d9f..06f1522a7256 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -41,7 +41,7 @@ import dagger.Provides;
* Provides dependencies from {@link com.android.wm.shell} which could be customized among different
* branches of SystemUI.
*/
-@Module(includes = {WMShellBaseModule.class, TvPipModule.class})
+@Module(includes = {TvPipModule.class})
public class TvWMShellModule {
@WMSingleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index be5ed0ccbef0..71a883d20988 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -16,14 +16,19 @@
package com.android.systemui.wmshell;
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+
+import android.animation.AnimationHandler;
import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.HandlerThread;
import android.view.IWindowManager;
import android.view.WindowManager;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.WMSingleton;
@@ -36,7 +41,6 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.common.AnimationThread;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -46,6 +50,9 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
+import com.android.wm.shell.common.annotations.ShellAnimationThread;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
@@ -62,6 +69,7 @@ import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
import dagger.BindsOptionalOf;
import dagger.Module;
@@ -74,6 +82,89 @@ import dagger.Provides;
@Module
public abstract class WMShellBaseModule {
+ private static final boolean ENABLE_SHELL_MAIN_THREAD = false;
+
+ //
+ // Shell Concurrency - Components used for managing threading in the Shell and SysUI
+ //
+
+ /**
+ * Provide a SysUI main-thread Executor.
+ */
+ @WMSingleton
+ @Provides
+ @Main
+ public static ShellExecutor provideSysUIMainExecutor(@Main Handler sysuiMainHandler) {
+ return new HandlerExecutor(sysuiMainHandler);
+ }
+
+ /**
+ * Shell main-thread Handler, don't use this unless really necessary (ie. need to dedupe
+ * multiple types of messages, etc.)
+ */
+ @WMSingleton
+ @Provides
+ @ShellMainThread
+ public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) {
+ if (ENABLE_SHELL_MAIN_THREAD) {
+ HandlerThread shellMainThread = new HandlerThread("wmshell.main");
+ shellMainThread.start();
+ return shellMainThread.getThreadHandler();
+ }
+ return sysuiMainHandler;
+ }
+
+ /**
+ * Provide a Shell main-thread Executor.
+ */
+ @WMSingleton
+ @Provides
+ @ShellMainThread
+ public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler shellMainHandler,
+ @Main ShellExecutor sysuiMainExecutor) {
+ if (ENABLE_SHELL_MAIN_THREAD) {
+ return new HandlerExecutor(shellMainHandler);
+ }
+ return sysuiMainExecutor;
+ }
+
+ /**
+ * Provide a Shell animation-thread Executor.
+ */
+ @WMSingleton
+ @Provides
+ @ShellAnimationThread
+ public static ShellExecutor provideShellAnimationExecutor() {
+ HandlerThread shellAnimationThread = new HandlerThread("wmshell.anim",
+ THREAD_PRIORITY_DISPLAY);
+ shellAnimationThread.start();
+ return new HandlerExecutor(shellAnimationThread.getThreadHandler());
+ }
+
+ /**
+ * Provide a Shell animation-thread AnimationHandler. The AnimationHandler can be set on
+ * {@link android.animation.ValueAnimator}s and will ensure that the animation will run on
+ * the Shell animation-thread.
+ */
+ @WMSingleton
+ @Provides
+ @ChoreographerSfVsync
+ public static AnimationHandler provideShellAnimationExecutorSfVsyncAnimationHandler(
+ @ShellAnimationThread ShellExecutor shellAnimationExecutor) {
+ try {
+ AnimationHandler handler = new AnimationHandler();
+ shellAnimationExecutor.executeBlocking(() -> {
+ // This is called on the animation thread since it calls
+ // Choreographer.getSfInstance() which returns a thread-local Choreographer instance
+ // that uses the SF vsync
+ handler.setProvider(new SfVsyncFrameCallbackProvider());
+ }, 1, TimeUnit.SECONDS);
+ return handler;
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to initialize SfVsync animation handler in 1s", e);
+ }
+ }
+
@WMSingleton
@Provides
static ShellInit provideShellInit(DisplayImeController displayImeController,
@@ -139,8 +230,9 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
- return new WindowManagerShellWrapper();
+ static WindowManagerShellWrapper provideWindowManagerShellWrapper(
+ @ShellMainThread ShellExecutor shellMainExecutor) {
+ return new WindowManagerShellWrapper(shellMainExecutor);
}
@WMSingleton
@@ -187,9 +279,11 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue,
- ShellExecutor mainExecutor, TransactionPool transactionPool, Context context) {
- return new ShellTaskOrganizer(syncQueue, transactionPool, mainExecutor,
- AnimationThread.instance().getExecutor(), context);
+ @ShellMainThread ShellExecutor shellMainExecutor,
+ @ShellAnimationThread ShellExecutor shellAnimationExecutor,
+ TransactionPool transactionPool, Context context) {
+ return new ShellTaskOrganizer(syncQueue, transactionPool, shellMainExecutor,
+ shellAnimationExecutor, context);
}
@WMSingleton
@@ -230,12 +324,6 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static ShellExecutor provideMainShellExecutor(@Main Handler handler) {
- return new HandlerExecutor(handler);
- }
-
- @WMSingleton
- @Provides
static Optional<HideDisplayCutout> provideHideDisplayCutoutController(Context context,
DisplayController displayController) {
return Optional.ofNullable(HideDisplayCutoutController.create(context, displayController));
@@ -262,5 +350,4 @@ public abstract class WMShellBaseModule {
static LetterboxConfigController provideLetterboxConfigController(Context context) {
return new LetterboxConfigController(context);
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 7a1c05890873..281b1aa9ad7b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -41,9 +42,9 @@ import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipController;
-import com.android.wm.shell.pip.phone.PipMenuActivityController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -93,13 +94,14 @@ public class WMShellModule {
static Optional<Pip> providePip(Context context, DisplayController displayController,
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
- PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer,
+ PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
- TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) {
+ TaskStackListenerImpl taskStackListener,
+ @ShellMainThread ShellExecutor shellMainExecutor) {
return Optional.ofNullable(PipController.create(context, displayController,
pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController,
- pipMenuActivityController, pipTaskOrganizer, pipTouchHandler,
- windowManagerShellWrapper, taskStackListener, mainExecutor));
+ phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
+ windowManagerShellWrapper, taskStackListener, shellMainExecutor));
}
@WMSingleton
@@ -117,21 +119,23 @@ public class WMShellModule {
@WMSingleton
@Provides
- static PipMenuActivityController providesPipMenuActivityController(Context context,
+ static PhonePipMenuController providesPipPhoneMenuController(Context context,
PipMediaController pipMediaController, SystemWindows systemWindows) {
- return new PipMenuActivityController(context, pipMediaController, systemWindows);
+ return new PhonePipMenuController(context, pipMediaController, systemWindows);
}
@WMSingleton
@Provides
static PipTouchHandler providePipTouchHandler(Context context,
- PipMenuActivityController menuActivityController, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
- PipUiEventLogger pipUiEventLogger) {
- return new PipTouchHandler(context, menuActivityController, pipBoundsAlgorithm,
- pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
+ PipUiEventLogger pipUiEventLogger,
+ @ShellMainThread ShellExecutor shellMainExecutor) {
+ return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm,
+ pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger,
+ shellMainExecutor);
}
@WMSingleton
@@ -139,12 +143,12 @@ public class WMShellModule {
static PipTaskOrganizer providePipTaskOrganizer(Context context,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
- PipMenuActivityController menuActivityController,
+ PhonePipMenuController menuPhoneController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm,
- menuActivityController, pipSurfaceTransactionHelper, splitScreenOptional,
+ menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional,
displayController, pipUiEventLogger, shellTaskOrganizer);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 4559113bcf54..a2eaea1a37c5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ContentResolver;
import android.content.res.Resources;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -72,6 +73,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
Resources mResources;
@Mock
NotificationIconAreaController mNotificationIconAreaController;
+ @Mock
+ ContentResolver mContentResolver;
private KeyguardClockSwitchController mController;
@@ -90,7 +93,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
mColorExtractor,
mClockManager,
mKeyguardSliceViewController,
- mNotificationIconAreaController);
+ mNotificationIconAreaController,
+ mContentResolver);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index a0ae35ffef00..11150432f757 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -29,20 +29,23 @@ import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_
import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -60,15 +63,18 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class MagnificationModeSwitchTest extends SysuiTestCase {
@@ -81,11 +87,10 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
private AccessibilityManager mAccessibilityManager;
@Mock
private WindowManager mWindowManager;
- @Mock
private ViewPropertyAnimator mViewPropertyAnimator;
private MagnificationModeSwitch mMagnificationModeSwitch;
- @Captor
- private ArgumentCaptor<View.OnTouchListener> mTouchListenerCaptor;
+ private View.OnTouchListener mTouchListener;
+ private List<MotionEvent> mMotionEvents = new ArrayList<>();
@Before
public void setUp() throws Exception {
@@ -97,9 +102,23 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
mSpyImageView = Mockito.spy(new ImageView(mContext));
- resetMockImageViewAndAnimator();
-
+ mViewPropertyAnimator = Mockito.spy(mSpyImageView.animate());
+ resetAndStubMockImageViewAndAnimator();
+ doAnswer((invocation) -> {
+ mTouchListener = invocation.getArgument(0);
+ return null;
+ }).when(mSpyImageView).setOnTouchListener(
+ any(View.OnTouchListener.class));
mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView);
+ assertNotNull(mTouchListener);
+ }
+
+ @After
+ public void tearDown() {
+ for (MotionEvent event:mMotionEvents) {
+ event.recycle();
+ }
+ mMotionEvents.clear();
}
@Test
@@ -124,7 +143,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
}
@Test
- public void showMagnificationButton_a11yTimeout_autoFadeOut() {
+ public void showMagnificationButton_setA11yTimeout_postDelayedAnimationWithA11yTimeout() {
final int a11yTimeout = 12345;
when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn(
a11yTimeout);
@@ -134,14 +153,21 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
verify(mAccessibilityManager).getRecommendedTimeoutMillis(
DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS
| AccessibilityManager.FLAG_CONTENT_CONTROLS);
- final ArgumentCaptor<Runnable> fadeOutCaptor = ArgumentCaptor.forClass(Runnable.class);
- final ArgumentCaptor<Long> fadeOutDelay = ArgumentCaptor.forClass(Long.class);
- verify(mSpyImageView).postOnAnimationDelayed(fadeOutCaptor.capture(),
- fadeOutDelay.capture());
- assertEquals(a11yTimeout, (long) fadeOutDelay.getValue());
+ verify(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), eq((long) a11yTimeout));
+ }
+
+ @Test
+ public void showMagnificationButton_windowMode_verifyAnimationEndAction() {
+ // Execute the runnable immediately to run the animation.
+ doAnswer((invocation) -> {
+ final Runnable action = invocation.getArgument(0);
+ action.run();
+ return null;
+ }).when(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), anyLong());
+
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
// Verify the end action after fade-out.
- fadeOutCaptor.getValue().run();
final ArgumentCaptor<Runnable> endActionCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(mViewPropertyAnimator).withEndAction(endActionCaptor.capture());
@@ -154,7 +180,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Test
public void onConfigurationChanged_buttonIsShowing_setImageResource() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- resetMockImageViewAndAnimator();
+ resetAndStubMockImageViewAndAnimator();
mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
@@ -165,42 +191,46 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Test
public void performSingleTap_fullscreenMode_removeViewAndChangeSettingsValue() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- resetMockImageViewAndAnimator();
+ resetAndStubMockImageViewAndAnimator();
// Perform a single-tap
- final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, 0, ACTION_DOWN, 100, 100, 0));
+ final long downTime = SystemClock.uptimeMillis();
+ mTouchListener.onTouch(mSpyImageView,
+ obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100));
+
verify(mViewPropertyAnimator).cancel();
- resetMockImageViewAndAnimator();
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, ViewConfiguration.getTapTimeout(), ACTION_UP, 100, 100, 0));
+ resetAndStubMockImageViewAndAnimator();
+ mTouchListener.onTouch(mSpyImageView,
+ obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100));
+
verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
@Test
- public void showMagnificationButton_performDragging_updateViewLayout() {
+ public void performDragging_showMagnificationButton_updateViewLayout() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- resetMockImageViewAndAnimator();
-
- // Perform dragging
- final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
- final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ resetAndStubMockImageViewAndAnimator();
final int previousMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, 0, ACTION_DOWN, 100, 100, 0));
+
+ // Perform dragging
+ final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10;
+ final long downTime = SystemClock.uptimeMillis();
+ mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
+ downTime, 0, ACTION_DOWN, 100, 100));
verify(mViewPropertyAnimator).cancel();
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0));
+ mTouchListener.onTouch(mSpyImageView,
+ obtainMotionEvent(downTime, downTime, ACTION_MOVE, 100 + offset,
+ 100));
verify(mWindowManager).updateViewLayout(eq(mSpyImageView),
any(WindowManager.LayoutParams.class));
- resetMockImageViewAndAnimator();
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0));
+ resetAndStubMockImageViewAndAnimator();
+ mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
+ downTime, downTime, ACTION_UP, 100 + offset, 100));
+
assertModeUnchanged(previousMode);
assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@@ -208,18 +238,17 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Test
public void performSingleTapActionCanceled_showButtonAnimation() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- resetMockImageViewAndAnimator();
-
- // Perform single tap
- final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+ resetAndStubMockImageViewAndAnimator();
final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, 0, ACTION_DOWN, 100, 100, 0));
- resetMockImageViewAndAnimator();
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0));
+ final long downTime = SystemClock.uptimeMillis();
+ mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
+ downTime, downTime, ACTION_DOWN, 100, 100));
+ resetAndStubMockImageViewAndAnimator();
+ mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
+ downTime, downTime, ACTION_CANCEL, 100, 100));
+
assertModeUnchanged(previousMode);
assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@@ -227,21 +256,21 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Test
public void performDraggingActionCanceled_showButtonAnimation() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- resetMockImageViewAndAnimator();
-
- // Perform dragging
- final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
- final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ resetAndStubMockImageViewAndAnimator();
final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, 0, ACTION_DOWN, 100, 100, 0));
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0));
-
- resetMockImageViewAndAnimator();
- listener.onTouch(mSpyImageView, MotionEvent.obtain(
- 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0));
+
+ // Perform dragging
+ final long downTime = SystemClock.uptimeMillis();
+ final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10;
+ mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
+ 0, 0, ACTION_DOWN, 100, 100));
+ mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
+ downTime, downTime, ACTION_MOVE, 100 + offset, 100));
+ resetAndStubMockImageViewAndAnimator();
+ mTouchListener.onTouch(mSpyImageView, obtainMotionEvent(
+ downTime, downTime, ACTION_CANCEL, 100 + offset, 100));
+
assertModeUnchanged(previousMode);
assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@@ -266,7 +295,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
@Test
public void performA11yActions_showWindowModeButton_verifyTapAction() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- resetMockImageViewAndAnimator();
+ resetAndStubMockImageViewAndAnimator();
mSpyImageView.performAccessibilityAction(
ACTION_CLICK.getId(), null);
@@ -278,7 +307,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
assertShowFadingAnimation(FADE_OUT_ALPHA);
- resetMockImageViewAndAnimator();
+ resetAndStubMockImageViewAndAnimator();
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -327,7 +356,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
} else { // Fade-out
verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong());
}
- resetMockAnimator();
+ resetAndStubMockAnimator();
runnableCaptor.getValue().run();
@@ -336,20 +365,15 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
verify(mViewPropertyAnimator).start();
}
- private void resetMockImageViewAndAnimator() {
+ private void resetAndStubMockImageViewAndAnimator() {
+ resetAndStubMockAnimator();
Mockito.reset(mSpyImageView);
- doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
- mTouchListenerCaptor.capture());
- resetMockAnimator();
+ doReturn(mViewPropertyAnimator).when(mSpyImageView).animate();
}
- private void resetMockAnimator() {
+ private void resetAndStubMockAnimator() {
Mockito.reset(mViewPropertyAnimator);
- when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
- when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
- when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
- mViewPropertyAnimator);
- when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator);
+ doNothing().when(mViewPropertyAnimator).start();
}
/**
@@ -366,4 +390,11 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT);
assertEquals(expectedMode, actualMode);
}
+
+ private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
+ float y) {
+ MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0);
+ mMotionEvents.add(event);
+ return event;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index f9b14ba7cd31..91144be7347d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -82,6 +82,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.text.NumberFormat;
import java.util.Collections;
@SmallTest
@@ -546,4 +547,68 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
pluggedIndication, powerIndication);
assertThat(mTextView.getText()).isEqualTo(pluggedIndication);
}
+
+ @Test
+ public void onRefreshBatteryInfo_chargingWithOverheat_presentChargingLimited() {
+ createController();
+ BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
+ 80 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
+ BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
+ true /* present */);
+
+ mController.getKeyguardCallback().onRefreshBatteryInfo(status);
+ mController.setVisible(true);
+
+ String percentage = NumberFormat.getPercentInstance().format(80 / 100f);
+ String pluggedIndication = mContext.getString(
+ R.string.keyguard_plugged_in_charging_limited, percentage);
+ assertThat(mTextView.getText()).isEqualTo(pluggedIndication);
+ }
+
+ @Test
+ public void onRefreshBatteryInfo_pluggedWithOverheat_presentChargingLimited() {
+ createController();
+ BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 80 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
+ BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
+ true /* present */);
+
+ mController.getKeyguardCallback().onRefreshBatteryInfo(status);
+ mController.setVisible(true);
+
+ String percentage = NumberFormat.getPercentInstance().format(80 / 100f);
+ String pluggedIndication = mContext.getString(
+ R.string.keyguard_plugged_in_charging_limited, percentage);
+ assertThat(mTextView.getText()).isEqualTo(pluggedIndication);
+ }
+
+ @Test
+ public void onRefreshBatteryInfo_fullChargedWithOverheat_presentCharged() {
+ createController();
+ BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
+ 100 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
+ BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
+ true /* present */);
+
+ mController.getKeyguardCallback().onRefreshBatteryInfo(status);
+ mController.setVisible(true);
+
+ String chargedIndication = mContext.getString(R.string.keyguard_charged);
+ assertThat(mTextView.getText()).isEqualTo(chargedIndication);
+ }
+
+ @Test
+ public void onRefreshBatteryInfo_dischargingWithOverheat_presentBatteryPercentage() {
+ createController();
+ BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 90 /* level */, 0 /* plugged */, BatteryManager.BATTERY_HEALTH_OVERHEAT,
+ 0 /* maxChargingWattage */, true /* present */);
+
+ mController.getKeyguardCallback().onRefreshBatteryInfo(status);
+ mController.setDozing(true);
+ mController.setVisible(true);
+
+ String percentage = NumberFormat.getPercentInstance().format(90 / 100f);
+ assertThat(mTextView.getText()).isEqualTo(percentage);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index 152c51e1f9f4..ac699f7192c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -65,7 +65,7 @@ public class RankingBuilder {
mKey = ranking.getKey();
mRank = ranking.getRank();
mMatchesInterruptionFilter = ranking.matchesInterruptionFilter();
- mVisibilityOverride = ranking.getVisibilityOverride();
+ mVisibilityOverride = ranking.getLockscreenVisibilityOverride();
mSuppressedVisualEffects = ranking.getSuppressedVisualEffects();
mImportance = ranking.getImportance();
mExplanation = ranking.getImportanceExplanation();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index fed190388ff0..d4a94a19af4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -270,7 +270,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
mMetricsLogger, mActivityManager, mConfigurationController,
- flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
+ () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mBiometricUnlockController, mStatusBarKeyguardViewManager,
mNotificationStackScrollLayoutController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 1f31fcd2a2bf..52b7b022bc8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -81,7 +81,9 @@ import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
@@ -261,11 +263,12 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
verify(mAssistManager).hideAssist();
- verify(mClickNotifier).onNotificationClick(
+ InOrder orderVerifier = Mockito.inOrder(mClickNotifier, mOnUserInteractionCallback);
+ orderVerifier.verify(mClickNotifier).onNotificationClick(
eq(sbn.getKey()), any(NotificationVisibility.class));
-
// Notification calls dismiss callback to remove notification due to FLAG_AUTO_CANCEL
- verify(mOnUserInteractionCallback).onDismiss(mNotificationRow.getEntry(), REASON_CLICK);
+ orderVerifier.verify(mOnUserInteractionCallback).onDismiss(mNotificationRow.getEntry(),
+ REASON_CLICK);
}
@Test
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index d6d4e4f6c746..8f093c7e6674 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1797,4 +1797,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region);
}
+
+ @Override
+ public void setFocusAppearance(int strokeWidth, int color) { }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index be2f8f16a412..c6919ad24572 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1968,6 +1968,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// Update the capabilities before the mode.
updateMagnificationCapabilitiesSettingsChangeLocked(userState);
updateMagnificationModeChangeSettingsLocked(userState);
+ updateFocusAppearanceDataLocked(userState);
}
private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) {
@@ -3012,6 +3013,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ /**
+ * Gets the stroke width of the focus rectangle.
+ * @return The stroke width.
+ */
+ public int getFocusStrokeWidth() {
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+
+ return userState.getFocusStrokeWidthLocked();
+ }
+ }
+
+ /**
+ * Gets the color of the focus rectangle.
+ * @return The color.
+ */
+ public int getFocusColor() {
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+
+ return userState.getFocusColorLocked();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
@@ -3623,4 +3648,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
}
+
+ private void updateFocusAppearanceDataLocked(AccessibilityUserState userState) {
+ if (userState.mUserId != mCurrentUserId) {
+ return;
+ }
+
+ mMainHandler.post(() -> {
+ broadcastToClients(userState, ignoreRemoteException(client -> {
+ client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
+ userState.getFocusColorLocked());
+ }));
+ });
+
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e48d11d17f40..5d67992316a2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -120,6 +120,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.removeServiceLocked(this);
+ userState.resetFocusAppearanceLocked();
+ mSystemSupport.onClientChangeLocked(false);
mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId);
mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), -1,
userState.mUserId);
@@ -144,6 +146,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
} finally {
Binder.restoreCallingIdentity(identity);
}
+ userState.resetFocusAppearanceLocked();
mSystemSupport.onClientChangeLocked(false);
}
}
@@ -310,6 +313,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState != null) {
userState.serviceDisconnectedLocked(this);
+ userState.resetFocusAppearanceLocked();
}
resetLocked();
mSystemSupport.getFullScreenMagnificationController().resetAllIfNeeded(mId);
@@ -391,4 +395,32 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
}
}
+
+ @Override
+ public void setFocusAppearance(int strokeWidth, int color) {
+ AccessibilityUserState userState = mUserStateWeakReference.get();
+ if (userState == null) {
+ return;
+ }
+
+ synchronized (mLock) {
+ if (!hasRightsToCurrentUserLocked()) {
+ return;
+ }
+
+ if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
+ return;
+ }
+
+ if (userState.getFocusStrokeWidthLocked() == strokeWidth
+ && userState.getFocusColorLocked() == color) {
+ return;
+ }
+
+ // Sets the appearance data in the A11yUserState.
+ userState.setFocusAppearanceLocked(strokeWidth, color);
+ // Updates the appearance data in the A11yManager.
+ mSystemSupport.onClientChangeLocked(false);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 240c7ff061c8..90e2fdfa2f03 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -45,6 +45,7 @@ import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
import java.io.FileDescriptor;
@@ -122,6 +123,15 @@ class AccessibilityUserState {
// The magnification capabilities used to know magnification mode could be switched.
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ /** The stroke width of the focus rectangle in pixels */
+ private int mFocusStrokeWidth;
+ /** The color of the focus rectangle */
+ private int mFocusColor;
+ // The default value of the focus stroke width.
+ private final int mFocusStrokeWidthDefaultValue;
+ // The default value of the focus color.
+ private final int mFocusColorDefaultValue;
+
private Context mContext;
@SoftKeyboardShowMode
@@ -140,6 +150,12 @@ class AccessibilityUserState {
mUserId = userId;
mContext = context;
mServiceInfoChangeListener = serviceInfoChangeListener;
+ mFocusStrokeWidthDefaultValue = mContext.getResources().getDimensionPixelSize(
+ R.dimen.accessibility_focus_highlight_stroke_width);
+ mFocusColorDefaultValue = mContext.getResources().getColor(
+ R.color.accessibility_focus_highlight_color);
+ mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
+ mFocusColor = mFocusColorDefaultValue;
}
boolean isHandlingAccessibilityEventsLocked() {
@@ -178,6 +194,7 @@ class AccessibilityUserState {
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ resetFocusAppearanceLocked();
}
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -880,4 +897,40 @@ class AccessibilityUserState {
}
return false;
}
+
+ /**
+ * Gets the stroke width of the focus rectangle.
+ * @return The stroke width.
+ */
+ public int getFocusStrokeWidthLocked() {
+ return mFocusStrokeWidth;
+ }
+
+ /**
+ * Gets the color of the focus rectangle.
+ * @return The color.
+ */
+ public int getFocusColorLocked() {
+ return mFocusColor;
+ }
+
+ /**
+ * Sets the stroke width and color of the focus rectangle.
+ *
+ * @param strokeWidth The strokeWidth of the focus rectangle.
+ * @param color The color of the focus rectangle.
+ */
+ public void setFocusAppearanceLocked(int strokeWidth, int color) {
+ mFocusStrokeWidth = strokeWidth;
+ mFocusColor = color;
+ }
+
+ /**
+ * Resets the stroke width and color of the focus rectangle to the default value.
+ *
+ */
+ public void resetFocusAppearanceLocked() {
+ mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
+ mFocusColor = mFocusColorDefaultValue;
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
index 721220729a33..d2c1bc10abb0 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java
@@ -106,13 +106,16 @@ public class WindowMagnificationPromptController {
final Notification.Builder notificationBuilder = new Notification.Builder(mContext,
SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION);
+ final String message = mContext.getString(R.string.window_magnification_prompt_content);
+
notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp)
.setContentTitle(mContext.getString(R.string.window_magnification_prompt_title))
- .setContentText(mContext.getString(R.string.window_magnification_prompt_content))
+ .setContentText(message)
.setLargeIcon(Icon.createWithResource(mContext,
R.drawable.ic_accessibility_magnification))
.setTicker(mContext.getString(R.string.window_magnification_prompt_title))
.setOnlyAlertOnce(true)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
.setDeleteIntent(createPendingIntent(ACTION_DISMISS))
.setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS))
.setActions(buildTurnOnAction(), buildDismissAction());
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f1988e9f8b6b..449063d95770 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -19,6 +19,7 @@ package com.android.server.appwidget;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.UserIdInt;
@@ -102,6 +103,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.View;
import android.widget.RemoteViews;
+
import com.android.internal.R;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -111,11 +113,14 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
import com.android.server.policy.IconUtilities;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -137,9 +142,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider,
OnCrossProfileWidgetProvidersChangeListener {
@@ -2497,85 +2499,80 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
- private static void serializeProvider(XmlSerializer out, Provider p) throws IOException {
+ private static void serializeProvider(TypedXmlSerializer out, Provider p) throws IOException {
out.startTag(null, "p");
out.attribute(null, "pkg", p.info.provider.getPackageName());
out.attribute(null, "cl", p.info.provider.getClassName());
- out.attribute(null, "tag", Integer.toHexString(p.tag));
+ out.attributeIntHex(null, "tag", p.tag);
if (!TextUtils.isEmpty(p.infoTag)) {
out.attribute(null, "info_tag", p.infoTag);
}
out.endTag(null, "p");
}
- private static void serializeHost(XmlSerializer out, Host host) throws IOException {
+ private static void serializeHost(TypedXmlSerializer out, Host host) throws IOException {
out.startTag(null, "h");
out.attribute(null, "pkg", host.id.packageName);
- out.attribute(null, "id", Integer.toHexString(host.id.hostId));
- out.attribute(null, "tag", Integer.toHexString(host.tag));
+ out.attributeIntHex(null, "id", host.id.hostId);
+ out.attributeIntHex(null, "tag", host.tag);
out.endTag(null, "h");
}
- private static void serializeAppWidget(XmlSerializer out, Widget widget,
+ private static void serializeAppWidget(TypedXmlSerializer out, Widget widget,
boolean saveRestoreCompleted) throws IOException {
out.startTag(null, "g");
- out.attribute(null, "id", Integer.toHexString(widget.appWidgetId));
- out.attribute(null, "rid", Integer.toHexString(widget.restoredId));
- out.attribute(null, "h", Integer.toHexString(widget.host.tag));
+ out.attributeIntHex(null, "id", widget.appWidgetId);
+ out.attributeIntHex(null, "rid", widget.restoredId);
+ out.attributeIntHex(null, "h", widget.host.tag);
if (widget.provider != null) {
- out.attribute(null, "p", Integer.toHexString(widget.provider.tag));
+ out.attributeIntHex(null, "p", widget.provider.tag);
}
if (widget.options != null) {
int minWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
int minHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
int maxWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int maxHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
- out.attribute(null, "min_width", Integer.toHexString((minWidth > 0) ? minWidth : 0));
- out.attribute(null, "min_height", Integer.toHexString((minHeight > 0) ? minHeight : 0));
- out.attribute(null, "max_width", Integer.toHexString((maxWidth > 0) ? maxWidth : 0));
- out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0));
- out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
- AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+ out.attributeIntHex(null, "min_width", (minWidth > 0) ? minWidth : 0);
+ out.attributeIntHex(null, "min_height", (minHeight > 0) ? minHeight : 0);
+ out.attributeIntHex(null, "max_width", (maxWidth > 0) ? maxWidth : 0);
+ out.attributeIntHex(null, "max_height", (maxHeight > 0) ? maxHeight : 0);
+ out.attributeIntHex(null, "host_category", widget.options.getInt(
+ AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY));
if (saveRestoreCompleted) {
boolean restoreCompleted = widget.options.getBoolean(
AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED);
- out.attribute(null, "restore_completed", Boolean.toString(restoreCompleted));
+ out.attributeBoolean(null, "restore_completed", restoreCompleted);
}
}
out.endTag(null, "g");
}
- private static Bundle parseWidgetIdOptions(XmlPullParser parser) {
+ private static Bundle parseWidgetIdOptions(TypedXmlPullParser parser) {
Bundle options = new Bundle();
- String restoreCompleted = parser.getAttributeValue(null, "restore_completed");
- if (restoreCompleted != null) {
- options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED,
- Boolean.valueOf(restoreCompleted));
- }
- String minWidthString = parser.getAttributeValue(null, "min_width");
- if (minWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
- Integer.parseInt(minWidthString, 16));
- }
- String minHeightString = parser.getAttributeValue(null, "min_height");
- if (minHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
- Integer.parseInt(minHeightString, 16));
- }
- String maxWidthString = parser.getAttributeValue(null, "max_width");
- if (maxWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
- Integer.parseInt(maxWidthString, 16));
- }
- String maxHeightString = parser.getAttributeValue(null, "max_height");
- if (maxHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
- Integer.parseInt(maxHeightString, 16));
- }
- String categoryString = parser.getAttributeValue(null, "host_category");
- if (categoryString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- Integer.parseInt(categoryString, 16));
+ boolean restoreCompleted = parser.getAttributeBoolean(null, "restore_completed", false);
+ if (restoreCompleted) {
+ options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED, true);
+ }
+ int minWidth = parser.getAttributeIntHex(null, "min_width", -1);
+ if (minWidth != -1) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth);
+ }
+ int minHeight = parser.getAttributeIntHex(null, "min_height", -1);
+ if (minHeight != -1) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight);
+ }
+ int maxWidth = parser.getAttributeIntHex(null, "max_width", -1);
+ if (maxWidth != -1) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth);
+ }
+ int maxHeight = parser.getAttributeIntHex(null, "max_height", -1);
+ if (maxHeight != -1) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight);
+ }
+ int category = parser.getAttributeIntHex(null, "host_category",
+ AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN);
+ if (category != AppWidgetProviderInfo.WIDGET_CATEGORY_UNKNOWN) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, category);
}
return options;
}
@@ -3080,7 +3077,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "gs");
- out.attribute(null, "version", String.valueOf(CURRENT_VERSION));
+ out.attributeInt(null, "version", CURRENT_VERSION);
N = mProviders.size();
for (int i = 0; i < N; i++) {
@@ -3149,12 +3146,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (type == XmlPullParser.START_TAG) {
String tag = parser.getName();
if ("gs".equals(tag)) {
- String attributeValue = parser.getAttributeValue(null, "version");
- try {
- version = Integer.parseInt(attributeValue);
- } catch (NumberFormatException e) {
- version = 0;
- }
+ version = parser.getAttributeInt(null, "version", 0);
} else if ("p".equals(tag)) {
legacyProviderIndex++;
// TODO: do we need to check that this package has the same signature
@@ -3193,9 +3185,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mProviders.add(provider);
}
- String tagAttribute = parser.getAttributeValue(null, "tag");
- final int providerTag = !TextUtils.isEmpty(tagAttribute)
- ? Integer.parseInt(tagAttribute, 16) : legacyProviderIndex;
+ final int providerTag = parser.getAttributeIntHex(null, "tag",
+ legacyProviderIndex);
provider.tag = providerTag;
provider.infoTag = parser.getAttributeValue(null, "info_tag");
@@ -3221,12 +3212,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (!host.zombie || mSafeMode) {
// In safe mode, we don't discard the hosts we don't recognize
// so that they're not pruned from our list. Otherwise, we do.
- final int hostId = Integer.parseInt(parser.getAttributeValue(
- null, "id"), 16);
-
- String tagAttribute = parser.getAttributeValue(null, "tag");
- final int hostTag = !TextUtils.isEmpty(tagAttribute)
- ? Integer.parseInt(tagAttribute, 16) : legacyHostIndex;
+ final int hostId = parser.getAttributeIntHex(null, "id");
+ final int hostTag = parser.getAttributeIntHex(null, "tag",
+ legacyHostIndex);
host.tag = hostTag;
host.id = new HostId(uid, hostId, pkg);
@@ -3241,21 +3229,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
} else if ("g".equals(tag)) {
Widget widget = new Widget();
- widget.appWidgetId = Integer.parseInt(parser.getAttributeValue(
- null, "id"), 16);
+ widget.appWidgetId = parser.getAttributeIntHex(null, "id");
setMinAppWidgetIdLocked(userId, widget.appWidgetId + 1);
// restored ID is allowed to be absent
- String restoredIdString = parser.getAttributeValue(null, "rid");
- widget.restoredId = (restoredIdString == null) ? 0
- : Integer.parseInt(restoredIdString, 16);
+ widget.restoredId = parser.getAttributeIntHex(null, "rid", 0);
widget.options = parseWidgetIdOptions(parser);
- final int hostTag = Integer.parseInt(parser.getAttributeValue(
- null, "h"), 16);
+ final int hostTag = parser.getAttributeIntHex(null, "h");
String providerString = parser.getAttributeValue(null, "p");
- final int providerTag = (providerString != null) ? Integer.parseInt(
- parser.getAttributeValue(null, "p"), 16) : TAG_UNDEFINED;
+ final int providerTag = (providerString != null)
+ ? parser.getAttributeIntHex(null, "p") : TAG_UNDEFINED;
// We can match widgets with hosts and providers only after hosts
// and providers for all users have been loaded since the widget
@@ -4372,11 +4356,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
try {
- XmlSerializer out = new FastXmlSerializer();
+ TypedXmlSerializer out = Xml.newFastSerializer();
out.setOutput(stream, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, "ws"); // widget state
- out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION));
+ out.attributeInt(null, "version", WIDGET_STATE_VERSION);
out.attribute(null, "pkg", backedupPackage);
// Remember all the providers that are currently hosted or published
@@ -4464,7 +4448,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// Hosts mentioned in the widget dataset by ordinal
ArrayList<Host> restoredHosts = new ArrayList<>();
- XmlPullParser parser = Xml.newPullParser();
+ TypedXmlPullParser parser = Xml.newFastPullParser();
parser.setInput(stream, StandardCharsets.UTF_8.name());
synchronized (mLock) {
@@ -4474,11 +4458,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (type == XmlPullParser.START_TAG) {
final String tag = parser.getName();
if ("ws".equals(tag)) {
- String version = parser.getAttributeValue(null, "version");
-
- final int versionNumber = Integer.parseInt(version);
+ final int versionNumber = parser.getAttributeInt(null, "version");
if (versionNumber > WIDGET_STATE_VERSION) {
- Slog.w(TAG, "Unable to process state version " + version);
+ Slog.w(TAG, "Unable to process state version " + versionNumber);
return;
}
@@ -4520,8 +4502,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
String pkg = parser.getAttributeValue(null, "pkg");
final int uid = getUidForPackage(pkg, userId);
- final int hostId = Integer.parseInt(
- parser.getAttributeValue(null, "id"), 16);
+ final int hostId = parser.getAttributeIntHex(null, "id");
HostId id = new HostId(uid, hostId, pkg);
Host h = lookupOrAddHostLocked(id);
@@ -4532,17 +4513,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
+ "]: {" + h.id + "}");
}
} else if ("g".equals(tag)) {
- int restoredId = Integer.parseInt(
- parser.getAttributeValue(null, "id"), 16);
- int hostIndex = Integer.parseInt(
- parser.getAttributeValue(null, "h"), 16);
+ int restoredId = parser.getAttributeIntHex(null, "id");
+ int hostIndex = parser.getAttributeIntHex(null, "h");
Host host = restoredHosts.get(hostIndex);
Provider p = null;
- String prov = parser.getAttributeValue(null, "p");
- if (prov != null) {
+ int which = parser.getAttributeIntHex(null, "p", -1);
+ if (which != -1) {
// could have been null if the app had allocated an id
// but not yet established a binding under that id
- int which = Integer.parseInt(prov, 16);
p = restoredProviders.get(which);
}
diff --git a/services/art-profile b/services/art-profile
index 23a2e16bdb5d..287e6094bfe0 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -32073,6 +32073,32 @@ PLcom/android/server/wm/AccessibilityController;->removeObserverOfEmbeddedDispla
PLcom/android/server/wm/AccessibilityController;->setMagnificationCallbacksLocked(ILcom/android/server/wm/WindowManagerInternal$MagnificationCallbacks;)Z
PLcom/android/server/wm/AccessibilityController;->setMagnificationSpecLocked(ILandroid/view/MagnificationSpec;)V
PLcom/android/server/wm/AccessibilityController;->setWindowsForAccessibilityCallbackLocked(ILcom/android/server/wm/WindowManagerInternal$WindowsForAccessibilityCallback;)Z
+HPLcom/android/server/wm/ActivityClientController;->activityDestroyed(Landroid/os/IBinder;)V
+HPLcom/android/server/wm/ActivityClientController;->activityIdle(Landroid/os/IBinder;Landroid/content/res/Configuration;Z)V
+HPLcom/android/server/wm/ActivityClientController;->activityPaused(Landroid/os/IBinder;)V
+PLcom/android/server/wm/ActivityClientController;->activityRelaunched(Landroid/os/IBinder;)V
+HPLcom/android/server/wm/ActivityClientController;->activityResumed(Landroid/os/IBinder;)V
+HPLcom/android/server/wm/ActivityClientController;->activityStopped(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/os/PersistableBundle;Ljava/lang/CharSequence;)V
+HPLcom/android/server/wm/ActivityClientController;->activityTopResumedStateLost()V
+PLcom/android/server/wm/ActivityClientController;->ensureValidPictureInPictureActivityParamsLocked(Ljava/lang/String;Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Lcom/android/server/wm/ActivityRecord;
+PLcom/android/server/wm/ActivityClientController;->enterPictureInPictureMode(Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Z
+HPLcom/android/server/wm/ActivityClientController;->getActivityOptions(Landroid/os/IBinder;)Landroid/os/Bundle;
+PLcom/android/server/wm/ActivityClientController;->getCallingRecord(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord;
+PLcom/android/server/wm/ActivityClientController;->isTopOfTask(Landroid/os/IBinder;)Z
+PLcom/android/server/wm/ActivityClientController;->moveActivityTaskToBack(Landroid/os/IBinder;Z)Z
+PLcom/android/server/wm/ActivityClientController;->navigateUpTo(Landroid/os/IBinder;Landroid/content/Intent;ILandroid/content/Intent;)Z
+HPLcom/android/server/wm/ActivityClientController;->notifyActivityDrawn(Landroid/os/IBinder;)V
+HPLcom/android/server/wm/ActivityClientController;->onBackPressedOnTaskRoot(Landroid/os/IBinder;)V
+PLcom/android/server/wm/ActivityClientController;->overridePendingTransition(Landroid/os/IBinder;Ljava/lang/String;II)V
+PLcom/android/server/wm/ActivityClientController;->registerRemoteAnimations(Landroid/os/IBinder;Landroid/view/RemoteAnimationDefinition;)V
+PLcom/android/server/wm/ActivityClientController;->releaseActivityInstance(Landroid/os/IBinder;)Z
+PLcom/android/server/wm/ActivityClientController;->reportActivityFullyDrawn(Landroid/os/IBinder;Z)V
+HPLcom/android/server/wm/ActivityClientController;->reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V
+PLcom/android/server/wm/ActivityClientController;->setDisablePreviewScreenshots(Landroid/os/IBinder;Z)V
+PLcom/android/server/wm/ActivityClientController;->setShowWhenLocked(Landroid/os/IBinder;Z)V
+HPLcom/android/server/wm/ActivityClientController;->setTaskDescription(Landroid/os/IBinder;Landroid/app/ActivityManager$TaskDescription;)V
+PLcom/android/server/wm/ActivityClientController;->setTurnScreenOn(Landroid/os/IBinder;Z)V
+PLcom/android/server/wm/ActivityClientController;->unregisterRemoteAnimations(Landroid/os/IBinder;)V
HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;-><init>()V
HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;->access$000(Lcom/android/server/wm/ActivityMetricsLogger$LaunchingState;)J
HSPLcom/android/server/wm/ActivityMetricsLogger$LaunchingState;->access$002(Lcom/android/server/wm/ActivityMetricsLogger$LaunchingState;J)J
@@ -32859,13 +32885,6 @@ HSPLcom/android/server/wm/ActivityTaskManagerService;->access$300(Lcom/android/s
HSPLcom/android/server/wm/ActivityTaskManagerService;->access$602(Lcom/android/server/wm/ActivityTaskManagerService;Lcom/android/server/wm/BackgroundActivityStartCallback;)Lcom/android/server/wm/BackgroundActivityStartCallback;
PLcom/android/server/wm/ActivityTaskManagerService;->access$802(Lcom/android/server/wm/ActivityTaskManagerService;Z)Z
HSPLcom/android/server/wm/ActivityTaskManagerService;->access$900(Lcom/android/server/wm/ActivityTaskManagerService;)Z
-HPLcom/android/server/wm/ActivityTaskManagerService;->activityDestroyed(Landroid/os/IBinder;)V
-HPLcom/android/server/wm/ActivityTaskManagerService;->activityIdle(Landroid/os/IBinder;Landroid/content/res/Configuration;Z)V
-HPLcom/android/server/wm/ActivityTaskManagerService;->activityPaused(Landroid/os/IBinder;)V
-PLcom/android/server/wm/ActivityTaskManagerService;->activityRelaunched(Landroid/os/IBinder;)V
-HPLcom/android/server/wm/ActivityTaskManagerService;->activityResumed(Landroid/os/IBinder;)V
-HPLcom/android/server/wm/ActivityTaskManagerService;->activityStopped(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/os/PersistableBundle;Ljava/lang/CharSequence;)V
-HPLcom/android/server/wm/ActivityTaskManagerService;->activityTopResumedStateLost()V
HSPLcom/android/server/wm/ActivityTaskManagerService;->addWindowLayoutReasons(I)V
HPLcom/android/server/wm/ActivityTaskManagerService;->applyUpdateLockStateLocked(Lcom/android/server/wm/ActivityRecord;)V
HPLcom/android/server/wm/ActivityTaskManagerService;->applyUpdateVrModeLocked(Lcom/android/server/wm/ActivityRecord;)V
@@ -32898,8 +32917,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->enforceNotIsolatedCaller(L
HPLcom/android/server/wm/ActivityTaskManagerService;->enforceTaskPermission(Ljava/lang/String;)V
PLcom/android/server/wm/ActivityTaskManagerService;->enqueueAssistContext(ILandroid/content/Intent;Ljava/lang/String;Landroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;ZZILandroid/os/Bundle;JI)Lcom/android/server/wm/ActivityTaskManagerService$PendingAssistExtras;
HSPLcom/android/server/wm/ActivityTaskManagerService;->ensureConfigAndVisibilityAfterUpdate(Lcom/android/server/wm/ActivityRecord;I)Z
-PLcom/android/server/wm/ActivityTaskManagerService;->ensureValidPictureInPictureActivityParamsLocked(Ljava/lang/String;Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Lcom/android/server/wm/ActivityRecord;
-PLcom/android/server/wm/ActivityTaskManagerService;->enterPictureInPictureMode(Landroid/os/IBinder;Landroid/app/PictureInPictureParams;)Z
PLcom/android/server/wm/ActivityTaskManagerService;->enterPictureInPictureMode(Lcom/android/server/wm/ActivityRecord;Landroid/app/PictureInPictureParams;)Z
PLcom/android/server/wm/ActivityTaskManagerService;->expireStartAsCallerTokenMsg(Landroid/os/IBinder;)V
HPLcom/android/server/wm/ActivityTaskManagerService;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
@@ -32909,7 +32926,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->finishSubActivity(Landroid/
PLcom/android/server/wm/ActivityTaskManagerService;->finishVoiceTask(Landroid/service/voice/IVoiceInteractionSession;)V
PLcom/android/server/wm/ActivityTaskManagerService;->forgetStartAsCallerTokenMsg(Landroid/os/IBinder;)V
PLcom/android/server/wm/ActivityTaskManagerService;->getActivityClassForToken(Landroid/os/IBinder;)Landroid/content/ComponentName;
-HPLcom/android/server/wm/ActivityTaskManagerService;->getActivityOptions(Landroid/os/IBinder;)Landroid/os/Bundle;
HSPLcom/android/server/wm/ActivityTaskManagerService;->getActivityStartController()Lcom/android/server/wm/ActivityStartController;
PLcom/android/server/wm/ActivityTaskManagerService;->getAllRootTaskInfosOnDisplay(I)Ljava/util/List;
HSPLcom/android/server/wm/ActivityTaskManagerService;->getAppInfoForUser(Landroid/content/pm/ApplicationInfo;I)Landroid/content/pm/ApplicationInfo;
@@ -32919,7 +32935,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->getAppWarningsLocked()Lcom
HSPLcom/android/server/wm/ActivityTaskManagerService;->getBackgroundActivityStartCallback()Lcom/android/server/wm/BackgroundActivityStartCallback;
PLcom/android/server/wm/ActivityTaskManagerService;->getCallingActivity(Landroid/os/IBinder;)Landroid/content/ComponentName;
PLcom/android/server/wm/ActivityTaskManagerService;->getCallingPackage(Landroid/os/IBinder;)Ljava/lang/String;
-PLcom/android/server/wm/ActivityTaskManagerService;->getCallingRecordLocked(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord;
PLcom/android/server/wm/ActivityTaskManagerService;->getConfiguration()Landroid/content/res/Configuration;
PLcom/android/server/wm/ActivityTaskManagerService;->getCurrentUserId()I
HPLcom/android/server/wm/ActivityTaskManagerService;->getDeviceConfigurationInfo()Landroid/content/pm/ConfigurationInfo;
@@ -32984,7 +32999,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->isKeyguardLocked()Z
HPLcom/android/server/wm/ActivityTaskManagerService;->isSameApp(ILjava/lang/String;)Z
HSPLcom/android/server/wm/ActivityTaskManagerService;->isSleepingLocked()Z
HPLcom/android/server/wm/ActivityTaskManagerService;->isSleepingOrShuttingDownLocked()Z
-PLcom/android/server/wm/ActivityTaskManagerService;->isTopOfTask(Landroid/os/IBinder;)Z
HPLcom/android/server/wm/ActivityTaskManagerService;->isUidForeground(I)Z
HPLcom/android/server/wm/ActivityTaskManagerService;->keyguardGoingAway(I)V
PLcom/android/server/wm/ActivityTaskManagerService;->lambda$4YLTqMi21jZ51BFcKX_h_gIoeGg(Lcom/android/server/wm/ActivityTaskManagerService;Ljava/util/Locale;)V
@@ -33000,33 +33014,22 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->lambda$setLockScreenShown$
PLcom/android/server/wm/ActivityTaskManagerService;->lambda$tPFRgtovnZu_2Zm4sCcLa9-oBto(Lcom/android/server/wm/ActivityTaskManagerService;Landroid/os/IBinder;)V
PLcom/android/server/wm/ActivityTaskManagerService;->lambda$uDPnzqVuuoVSFA7RJcXFWsrCwrY(Lcom/android/server/wm/ActivityTaskManagerService;ILandroid/content/res/Configuration;)V
PLcom/android/server/wm/ActivityTaskManagerService;->logAppTooSlow(Lcom/android/server/wm/WindowProcessController;JLjava/lang/String;)V
-PLcom/android/server/wm/ActivityTaskManagerService;->moveActivityTaskToBack(Landroid/os/IBinder;Z)Z
PLcom/android/server/wm/ActivityTaskManagerService;->moveTaskToFrontLocked(Landroid/app/IApplicationThread;Ljava/lang/String;IILcom/android/server/wm/SafeActivityOptions;)V
-PLcom/android/server/wm/ActivityTaskManagerService;->navigateUpTo(Landroid/os/IBinder;Landroid/content/Intent;ILandroid/content/Intent;)Z
-HPLcom/android/server/wm/ActivityTaskManagerService;->notifyActivityDrawn(Landroid/os/IBinder;)V
-HSPLcom/android/server/wm/ActivityTaskManagerService;->notifyEnterAnimationComplete(Landroid/os/IBinder;)V
-PLcom/android/server/wm/ActivityTaskManagerService;->notifyLaunchTaskBehindComplete(Landroid/os/IBinder;)V
PLcom/android/server/wm/ActivityTaskManagerService;->notifyTaskPersisterLocked(Lcom/android/server/wm/Task;Z)V
HSPLcom/android/server/wm/ActivityTaskManagerService;->onActivityManagerInternalAdded()V
-HPLcom/android/server/wm/ActivityTaskManagerService;->onBackPressedOnTaskRoot(Landroid/os/IBinder;)V
HSPLcom/android/server/wm/ActivityTaskManagerService;->onInitPowerManagement()V
HPLcom/android/server/wm/ActivityTaskManagerService;->onScreenAwakeChanged(Z)V
HSPLcom/android/server/wm/ActivityTaskManagerService;->onStartActivitySetDidAppSwitch()V
HSPLcom/android/server/wm/ActivityTaskManagerService;->onSystemReady()V
-PLcom/android/server/wm/ActivityTaskManagerService;->overridePendingTransition(Landroid/os/IBinder;Ljava/lang/String;II)V
PLcom/android/server/wm/ActivityTaskManagerService;->pendingAssistExtrasTimedOut(Lcom/android/server/wm/ActivityTaskManagerService$PendingAssistExtras;)V
PLcom/android/server/wm/ActivityTaskManagerService;->postFinishBooting(ZZ)V
PLcom/android/server/wm/ActivityTaskManagerService;->registerRemoteAnimationForNextActivityStart(Ljava/lang/String;Landroid/view/RemoteAnimationAdapter;)V
-PLcom/android/server/wm/ActivityTaskManagerService;->registerRemoteAnimations(Landroid/os/IBinder;Landroid/view/RemoteAnimationDefinition;)V
HPLcom/android/server/wm/ActivityTaskManagerService;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
PLcom/android/server/wm/ActivityTaskManagerService;->relaunchReasonToString(I)Ljava/lang/String;
-PLcom/android/server/wm/ActivityTaskManagerService;->releaseActivityInstance(Landroid/os/IBinder;)Z
PLcom/android/server/wm/ActivityTaskManagerService;->releaseSomeActivities(Landroid/app/IApplicationThread;)V
PLcom/android/server/wm/ActivityTaskManagerService;->removeRootTasksInWindowingModes([I)V
PLcom/android/server/wm/ActivityTaskManagerService;->removeTask(I)Z
-PLcom/android/server/wm/ActivityTaskManagerService;->reportActivityFullyDrawn(Landroid/os/IBinder;Z)V
PLcom/android/server/wm/ActivityTaskManagerService;->reportAssistContextExtras(Landroid/os/IBinder;Landroid/os/Bundle;Landroid/app/assist/AssistStructure;Landroid/app/assist/AssistContent;Landroid/net/Uri;)V
-HPLcom/android/server/wm/ActivityTaskManagerService;->reportSizeConfigurations(Landroid/os/IBinder;[I[I[I)V
PLcom/android/server/wm/ActivityTaskManagerService;->requestAssistContextExtras(ILandroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;ZZ)Z
PLcom/android/server/wm/ActivityTaskManagerService;->requestAutofillData(Landroid/app/IAssistDataReceiver;Landroid/os/Bundle;Landroid/os/IBinder;I)Z
PLcom/android/server/wm/ActivityTaskManagerService;->requestStartActivityPermissionToken(Landroid/os/IBinder;)Landroid/os/IBinder;
@@ -33039,7 +33042,6 @@ PLcom/android/server/wm/ActivityTaskManagerService;->sendPutConfigurationForUser
PLcom/android/server/wm/ActivityTaskManagerService;->setBooted(Z)V
PLcom/android/server/wm/ActivityTaskManagerService;->setBooting(Z)V
HSPLcom/android/server/wm/ActivityTaskManagerService;->setDeviceOwnerUid(I)V
-PLcom/android/server/wm/ActivityTaskManagerService;->setDisablePreviewScreenshots(Landroid/os/IBinder;Z)V
PLcom/android/server/wm/ActivityTaskManagerService;->setFocusedTask(I)V
PLcom/android/server/wm/ActivityTaskManagerService;->setImmersive(Landroid/os/IBinder;Z)V
HPLcom/android/server/wm/ActivityTaskManagerService;->setLockScreenShown(ZZ)V
@@ -33047,10 +33049,7 @@ PLcom/android/server/wm/ActivityTaskManagerService;->setPictureInPictureParams(L
HSPLcom/android/server/wm/ActivityTaskManagerService;->setRecentTasks(Lcom/android/server/wm/RecentTasks;)V
PLcom/android/server/wm/ActivityTaskManagerService;->setRequestedOrientation(Landroid/os/IBinder;I)V
HPLcom/android/server/wm/ActivityTaskManagerService;->setResumedActivityUncheckLocked(Lcom/android/server/wm/ActivityRecord;Ljava/lang/String;)V
-PLcom/android/server/wm/ActivityTaskManagerService;->setShowWhenLocked(Landroid/os/IBinder;Z)V
PLcom/android/server/wm/ActivityTaskManagerService;->setSplitScreenResizing(Z)V
-HPLcom/android/server/wm/ActivityTaskManagerService;->setTaskDescription(Landroid/os/IBinder;Landroid/app/ActivityManager$TaskDescription;)V
-PLcom/android/server/wm/ActivityTaskManagerService;->setTurnScreenOn(Landroid/os/IBinder;Z)V
HSPLcom/android/server/wm/ActivityTaskManagerService;->setUsageStatsManager(Landroid/app/usage/UsageStatsManagerInternal;)V
HSPLcom/android/server/wm/ActivityTaskManagerService;->setWindowManager(Lcom/android/server/wm/WindowManagerService;)V
PLcom/android/server/wm/ActivityTaskManagerService;->shouldDisableNonVrUiLocked()Z
@@ -33070,7 +33069,6 @@ HPLcom/android/server/wm/ActivityTaskManagerService;->startTimeTrackingFocusedAc
PLcom/android/server/wm/ActivityTaskManagerService;->stopAppSwitches()V
PLcom/android/server/wm/ActivityTaskManagerService;->stopLockTaskModeInternal(Landroid/os/IBinder;Z)V
PLcom/android/server/wm/ActivityTaskManagerService;->stopSystemLockTaskMode()V
-PLcom/android/server/wm/ActivityTaskManagerService;->unregisterRemoteAnimations(Landroid/os/IBinder;)V
HPLcom/android/server/wm/ActivityTaskManagerService;->unregisterTaskStackListener(Landroid/app/ITaskStackListener;)V
HPLcom/android/server/wm/ActivityTaskManagerService;->updateActivityUsageStats(Lcom/android/server/wm/ActivityRecord;I)V
HPLcom/android/server/wm/ActivityTaskManagerService;->updateBatteryStats(Lcom/android/server/wm/ActivityRecord;Z)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 32126987376a..33d13de8be4b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1138,7 +1138,7 @@ final class AutofillManagerServiceImpl
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
final Session session = mSessions.valueAt(i);
- if (session.isSavingLocked()) {
+ if (session.isSaveUiShowingLocked()) {
if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id);
session.forceRemoveFromServiceLocked();
} else {
@@ -1660,7 +1660,7 @@ final class AutofillManagerServiceImpl
if (sessionToRemove != null && sessionsToRemove.valueAt(i)
== sessionToRemove.getActivityTokenLocked()) {
- if (sessionToRemove.isSavingLocked()) {
+ if (sessionToRemove.isSaveUiShowingLocked()) {
if (sVerbose) {
Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b48d71a51ce3..9d8901adbc9c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -39,6 +39,7 @@ import static com.android.server.autofill.Helper.toArray;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -113,6 +114,8 @@ import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -153,6 +156,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private static AtomicInteger sIdCounter = new AtomicInteger(2);
+ @GuardedBy("mLock")
+ private @SessionState int mSessionState = STATE_UNKNOWN;
+
+ /** Session state uninitiated. */
+ public static final int STATE_UNKNOWN = 0;
+
+ /** Session is active for filling. */
+ public static final int STATE_ACTIVE = 1;
+
+ /** Session finished for filling, staying alive for saving. */
+ public static final int STATE_FINISHED = 2;
+
+ /** Session is destroyed and removed from the manager service. */
+ public static final int STATE_REMOVED = 3;
+
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_UNKNOWN,
+ STATE_ACTIVE,
+ STATE_FINISHED,
+ STATE_REMOVED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SessionState{}
+
+ @GuardedBy("mLock")
+ private final SessionFlags mSessionFlags;
+
/**
* ID of the session.
*
@@ -236,10 +266,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private boolean mDestroyed;
- /** Whether the session is currently saving. */
- @GuardedBy("mLock")
- private boolean mIsSaving;
-
/**
* Helper used to handle state of Save UI when it must be hiding to show a custom description
* link and later recovered.
@@ -270,9 +296,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private final LocalLog mWtfHistory;
- @GuardedBy("mLock")
- private boolean mExpiredResponse;
-
/**
* Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id.
*/
@@ -307,13 +330,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private ArrayList<AutofillId> mAugmentedAutofillableIds;
- /**
- * When {@code true}, the session was created only to handle Augmented Autofill requests (i.e.,
- * the session would not have existed otherwsie).
- */
- @GuardedBy("mLock")
- private boolean mForAugmentedAutofillOnly;
-
@Nullable
private final AutofillInlineSessionController mInlineSessionController;
@@ -327,18 +343,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// returns null, and augmented autofill is triggered, and then the user switches the input
// method. Tapping on the field again will not trigger a new augmented autofill request.
// This may be fixed by adding more checks such as whether mCurrentViewId is null.
- if (mExpiredResponse) {
+ if (mSessionFlags.mExpiredResponse) {
return;
}
if (shouldResetSessionStateOnInputMethodSwitch()) {
// Set the old response expired, so the next action (ACTION_VIEW_ENTERED) can trigger
// a new fill request.
- mExpiredResponse = true;
+ mSessionFlags.mExpiredResponse = true;
// Clear the augmented autofillable ids so augmented autofill will trigger again.
mAugmentedAutofillableIds = null;
// In case the field is augmented autofill only, we clear the current view id, so that
// we won't skip view entered due to same view entered, for the augmented autofill.
- if (mForAugmentedAutofillOnly) {
+ if (mSessionFlags.mAugmentedAutofillOnly) {
mCurrentViewId = null;
}
}
@@ -356,7 +372,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return false;
}
- if (isInlineSuggestionsEnabledByAutofillProviderLocked()) {
+ if (mSessionFlags.mInlineSupportedByService) {
return true;
}
@@ -370,6 +386,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
+ * Collection of flags/booleans that helps determine Session behaviors.
+ */
+ private final class SessionFlags {
+ /** Whether autofill is disabled by the service */
+ @GuardedBy("mLock")
+ private boolean mAutofillDisabled;
+
+ /** Whether the autofill service supports inline suggestions */
+ @GuardedBy("mLock")
+ private boolean mInlineSupportedByService;
+
+ /** True if session is for augmented only */
+ @GuardedBy("mLock")
+ private boolean mAugmentedAutofillOnly;
+
+ /** Whether the session is currently showing the SaveUi. */
+ @GuardedBy("mLock")
+ private boolean mShowingSaveUi;
+
+ /** Whether the current {@link FillResponse} is expired. */
+ @GuardedBy("mLock")
+ private boolean mExpiredResponse;
+ }
+
+ /**
* TODO(b/151867668): improve how asynchronous data dependencies are handled, without using
* CountDownLatch.
*/
@@ -423,7 +464,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void onHandleAssistData(Bundle resultData) throws RemoteException {
if (mRemoteFillService == null) {
wtf(null, "onHandleAssistData() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly);
+ + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
return;
}
// Keeps to prevent it is cleared on multiple threads.
@@ -685,7 +726,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private void cancelCurrentRequestLocked() {
if (mRemoteFillService == null) {
wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly);
+ + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
return;
}
final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
@@ -705,14 +746,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- /**
- * Returns whether inline suggestions are supported by Autofill provider (not augmented
- * Autofill provider).
- */
- private boolean isInlineSuggestionsEnabledByAutofillProviderLocked() {
- return mService.isInlineSuggestionsEnabledLocked();
- }
-
private boolean isViewFocusedLocked(int flags) {
return (flags & FLAG_VIEW_NOT_FOCUSED) == 0;
}
@@ -733,14 +766,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ViewState.STATE_INITIAL,
/* clearResponse= */ true);
}
- mExpiredResponse = false;
- if (mForAugmentedAutofillOnly || mRemoteFillService == null) {
+ mSessionFlags.mExpiredResponse = false;
+ mSessionState = STATE_ACTIVE;
+ if (mSessionFlags.mAugmentedAutofillOnly || mRemoteFillService == null) {
if (sVerbose) {
Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead "
- + "(mForAugmentedAutofillOnly=" + mForAugmentedAutofillOnly
+ + "(mForAugmentedAutofillOnly=" + mSessionFlags.mAugmentedAutofillOnly
+ ", flags=" + flags + ")");
}
- mForAugmentedAutofillOnly = true;
+ mSessionFlags.mAugmentedAutofillOnly = true;
triggerAugmentedAutofillLocked(flags);
return;
}
@@ -779,7 +813,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// is also not focused.
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if (isInlineSuggestionsEnabledByAutofillProviderLocked()
+ if (mSessionFlags.mInlineSupportedByService
&& remoteRenderService != null
&& isViewFocusedLocked(flags)) {
Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
@@ -849,8 +883,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mWtfHistory = wtfHistory;
mComponentName = componentName;
mCompatMode = compatMode;
- mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
- setClientLocked(client);
+ mSessionState = STATE_ACTIVE;
+ synchronized (mLock) {
+ mSessionFlags = new SessionFlags();
+ mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
+ mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
+ setClientLocked(client);
+ }
mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal,
userId, componentName, handler, mLock,
@@ -906,9 +945,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
unlinkClientVultureLocked();
mClient = IAutoFillManagerClient.Stub.asInterface(client);
mClientVulture = () -> {
- Slog.d(TAG, "handling death of " + mActivityToken + " when saving=" + mIsSaving);
synchronized (mLock) {
- if (mIsSaving) {
+ Slog.d(TAG, "handling death of " + mActivityToken + " when saving="
+ + mSessionFlags.mShowingSaveUi);
+ if (mSessionFlags.mShowingSaveUi) {
mUi.hideFillUi(this);
} else {
mUi.destroyAll(mPendingSaveUi, this, false);
@@ -973,9 +1013,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.setLastResponse(id, response);
- int sessionFinishedState = 0;
final long disableDuration = response.getDisableDuration();
- if (disableDuration > 0) {
+ final boolean autofillDisabled = disableDuration > 0;
+ if (autofillDisabled) {
final int flags = response.getFlags();
final boolean disableActivityOnly =
(flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0;
@@ -990,15 +1030,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
id, mCompatMode);
}
- // Although "standard" autofill is disabled, it might still trigger augmented autofill
- if (triggerAugmentedAutofillLocked(requestFlags) != null) {
- mForAugmentedAutofillOnly = true;
- if (sDebug) {
- Slog.d(TAG, "Service disabled autofill for " + mComponentName
- + ", but session is kept for augmented autofill only");
+ synchronized (mLock) {
+ mSessionFlags.mAutofillDisabled = true;
+
+ // Although "standard" autofill is disabled, it might still trigger augmented
+ // autofill
+ if (triggerAugmentedAutofillLocked(requestFlags) != null) {
+ mSessionFlags.mAugmentedAutofillOnly = true;
+ if (sDebug) {
+ Slog.d(TAG, "Service disabled autofill for " + mComponentName
+ + ", but session is kept for augmented autofill only");
+ }
+ return;
}
- return;
}
+
if (sDebug) {
final StringBuilder message = new StringBuilder("Service disabled autofill for ")
.append(mComponentName)
@@ -1007,14 +1053,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
TimeUtils.formatDuration(disableDuration, message);
Slog.d(TAG, message.toString());
}
- sessionFinishedState = AutofillManager.STATE_DISABLED_BY_SERVICE;
}
if (((response.getDatasets() == null || response.getDatasets().isEmpty())
&& response.getAuthentication() == null)
- || disableDuration > 0) {
+ || autofillDisabled) {
// Response is "empty" from an UI point of view, need to notify client.
- notifyUnavailableToClient(sessionFinishedState, /* autofillableIds= */ null);
+ notifyUnavailableToClient(
+ autofillDisabled ? AutofillManager.STATE_DISABLED_BY_SERVICE : 0,
+ /* autofillableIds= */ null);
synchronized (mLock) {
mInlineSessionController.setInlineFillUiLocked(
InlineFillUi.emptyUi(mCurrentViewId));
@@ -1094,7 +1141,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void onSaveRequestSuccess(@NonNull String servicePackageName,
@Nullable IntentSender intentSender) {
synchronized (mLock) {
- mIsSaving = false;
+ mSessionFlags.mShowingSaveUi = false;
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: "
@@ -1120,7 +1167,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@NonNull String servicePackageName) {
boolean showMessage = !TextUtils.isEmpty(message);
synchronized (mLock) {
- mIsSaving = false;
+ mSessionFlags.mShowingSaveUi = false;
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: "
@@ -1246,7 +1293,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void cancelSave() {
synchronized (mLock) {
- mIsSaving = false;
+ mSessionFlags.mShowingSaveUi = false;
if (mDestroyed) {
Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
@@ -1424,7 +1471,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// The client becomes invisible for the authentication, the response is effective.
- mExpiredResponse = false;
+ mSessionFlags.mExpiredResponse = false;
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
@@ -1996,6 +2043,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
Event.NO_SAVE_REASON_NONE);
}
+ mSessionState = STATE_FINISHED;
final FillResponse response = getLastResponseLocked("showSaveLocked(%s)");
final SaveInfo saveInfo = response == null ? null : response.getSaveInfo();
@@ -2267,7 +2315,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.e(TAG, "Error notifying client to set save UI state to shown: " + e);
}
}
- mIsSaving = true;
+ mSessionFlags.mShowingSaveUi = true;
return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false,
Event.NO_SAVE_REASON_NONE);
}
@@ -2316,8 +2364,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Returns whether the session is currently showing the save UI
*/
@GuardedBy("mLock")
- boolean isSavingLocked() {
- return mIsSaving;
+ boolean isSaveUiShowingLocked() {
+ return mSessionFlags.mShowingSaveUi;
}
/**
@@ -2435,7 +2483,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (mRemoteFillService == null) {
wtf(null, "callSaveLocked() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly);
+ + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
return;
}
@@ -2540,7 +2588,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
- mForAugmentedAutofillOnly = false;
+ mSessionFlags.mAugmentedAutofillOnly = false;
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags);
return true;
@@ -2578,7 +2626,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
& ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0;
}
- if (mExpiredResponse) {
+ if (mSessionFlags.mExpiredResponse) {
if (sDebug) {
Slog.d(TAG, "Starting a new partition because the response has expired.");
}
@@ -2637,7 +2685,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
if (action == ACTION_RESPONSE_EXPIRED) {
- mExpiredResponse = true;
+ mSessionFlags.mExpiredResponse = true;
if (sDebug) {
Slog.d(TAG, "Set the response has expired.");
}
@@ -2652,7 +2700,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ViewState viewState = mViewStates.get(id);
if (sVerbose) {
Slog.v(TAG, "updateLocked(" + this.id + "): mCurrentViewId=" + mCurrentViewId
- + ", mExpiredResponse=" + mExpiredResponse + ", viewState=" + viewState);
+ + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse
+ + ", viewState=" + viewState);
}
if (viewState == null) {
@@ -2753,7 +2802,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
}
return;
- } else if (mForAugmentedAutofillOnly && isSameViewEntered) {
+ } else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) {
// Regular autofill is disabled.
if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
return;
@@ -3369,9 +3418,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService != null
- && (mForAugmentedAutofillOnly
- || !isInlineSuggestionsEnabledByAutofillProviderLocked()
- || mExpiredResponse)
+ && (mSessionFlags.mAugmentedAutofillOnly
+ || !mSessionFlags.mInlineSupportedByService
+ || mSessionFlags.mExpiredResponse)
&& isViewFocusedLocked(flags)) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback(
@@ -3694,7 +3743,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public String toString() {
- return "Session: [id=" + id + ", component=" + mComponentName + "]";
+ return "Session: [id=" + id + ", component=" + mComponentName
+ + ", state=" + sessionStateAsString(mSessionState) + "]";
}
@GuardedBy("mLock")
@@ -3704,6 +3754,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.print(prefix); pw.print("uid: "); pw.println(uid);
pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
pw.print(prefix); pw.print("flags: "); pw.println(mFlags);
+ pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState));
pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime);
@@ -3734,7 +3785,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
- pw.print(prefix); pw.print("mIsSaving: "); pw.println(mIsSaving);
+ pw.print(prefix); pw.print("mShowingSaveUi: "); pw.println(mSessionFlags.mShowingSaveUi);
pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi);
final int numberViews = mViewStates.size();
pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
@@ -3778,7 +3829,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
mSaveOnAllViewsInvisible);
pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
- if (mForAugmentedAutofillOnly) {
+ if (mSessionFlags.mAugmentedAutofillOnly) {
pw.print(prefix); pw.println("For Augmented Autofill Only");
}
if (mAugmentedAutofillDestroyer != null) {
@@ -3967,7 +4018,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS,
totalAugmentedRequests);
}
- if (mForAugmentedAutofillOnly) {
+ if (mSessionFlags.mAugmentedAutofillOnly) {
log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1);
}
mMetricsLogger.write(log);
@@ -3988,9 +4039,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
void forceRemoveFromServiceIfForAugmentedOnlyLocked() {
if (sVerbose) {
Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): "
- + mForAugmentedAutofillOnly);
+ + mSessionFlags.mAugmentedAutofillOnly);
}
- if (!mForAugmentedAutofillOnly) return;
+ if (!mSessionFlags.mAugmentedAutofillOnly) return;
forceRemoveFromServiceLocked();
}
@@ -4052,6 +4103,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (remoteFillService != null) {
remoteFillService.destroy();
}
+ mSessionState = STATE_REMOVED;
}
void onPendingSaveUi(int operation, @NonNull IBinder token) {
@@ -4168,4 +4220,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return "UNKNOWN_" + action;
}
}
+
+ private static String sessionStateAsString(@SessionState int sessionState) {
+ switch (sessionState) {
+ case STATE_UNKNOWN:
+ return "STATE_UNKNOWN";
+ case STATE_ACTIVE:
+ return "STATE_ACTIVE";
+ case STATE_FINISHED:
+ return "STATE_FINISHED";
+ case STATE_REMOVED:
+ return "STATE_REMOVED";
+ default:
+ return "UNKNOWN_SESSION_STATE_" + sessionState;
+ }
+ }
}
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index b5444f485a1c..68376c5ad178 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -10,5 +10,5 @@ java_library_static {
defaults: ["platform_service_defaults"],
srcs: [":services.backup-sources"],
libs: ["services.core"],
- static_libs: ["backuplib"],
+ static_libs: ["backuplib", "app-compat-annotations"],
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2ff66b564ec9..136cd22fad83 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -3030,9 +3030,11 @@ public class UserBackupManagerService {
}
Slog.i(TAG, addUserIdToLogMessage(mUserId, "Beginning adb backup..."));
+ BackupEligibilityRules eligibilityRules = getEligibilityRulesForOperation(
+ OperationType.ADB_BACKUP);
AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
- pkgList, mScheduledBackupEligibility);
+ pkgList, eligibilityRules);
final int token = generateRandomIntegerToken();
synchronized (mAdbBackupRestoreConfirmations) {
mAdbBackupRestoreConfirmations.put(token, params);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index c94286ffcffa..e03150eb824f 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -24,6 +24,7 @@ import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import android.app.backup.BackupManager;
import android.app.backup.IFullBackupRestoreObserver;
import android.content.pm.PackageManagerInternal;
import android.os.ParcelFileDescriptor;
@@ -104,11 +105,13 @@ public class PerformAdbRestoreTask implements Runnable {
return;
}
+ BackupEligibilityRules eligibilityRules = new BackupEligibilityRules(
+ mBackupManagerService.getPackageManager(),
+ LocalServices.getService(PackageManagerInternal.class),
+ mBackupManagerService.getUserId(), BackupManager.OperationType.ADB_BACKUP);
FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null,
mObserver, null, null, true, 0 /*unused*/, true,
- BackupEligibilityRules.forBackup(mBackupManagerService.getPackageManager(),
- LocalServices.getService(PackageManagerInternal.class),
- mBackupManagerService.getUserId()));
+ eligibilityRules);
FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine,
tarInputStream);
mEngineThread.run();
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 73ba1f19c092..2078492e67ab 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -25,12 +25,16 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.BackupTransport;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.app.compat.CompatChanges;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
+import android.os.Build;
import android.os.UserHandle;
import android.util.Slog;
@@ -41,6 +45,7 @@ import com.android.server.backup.transport.TransportClient;
import com.google.android.collect.Sets;
+import java.util.Objects;
import java.util.Set;
/**
@@ -57,6 +62,15 @@ public class BackupEligibilityRules {
private final int mUserId;
@OperationType private final int mOperationType;
+ /**
+ * When this change is enabled, {@code adb backup} is automatically turned on for apps
+ * running as debuggable ({@code android:debuggable} set to {@code true}) and unavailable to
+ * any other apps.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ static final long RESTRICT_ADB_BACKUP = 171032338L;
+
public static BackupEligibilityRules forBackup(PackageManager packageManager,
PackageManagerInternal packageManagerInternal,
int userId) {
@@ -134,12 +148,53 @@ public class BackupEligibilityRules {
* @return boolean indicating whether backup is allowed.
*/
public boolean isAppBackupAllowed(ApplicationInfo app) {
- if (mOperationType == OperationType.MIGRATION && !UserHandle.isCore(app.uid)) {
- // Backup / restore of all apps is force allowed during device-to-device migration.
- return true;
- }
+ boolean isSystemApp = UserHandle.isCore(app.uid);
+ boolean allowBackup = (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
+ switch (mOperationType) {
+ case OperationType.MIGRATION:
+ // Backup / restore of all non-system apps is force allowed during
+ // device-to-device migration.
+ return !isSystemApp || allowBackup;
+ case OperationType.ADB_BACKUP:
+ String packageName = app.packageName;
+ if (packageName == null) {
+ Slog.w(TAG, "Invalid ApplicationInfo object");
+ return false;
+ }
+
+ if (!CompatChanges.isChangeEnabled(RESTRICT_ADB_BACKUP, packageName,
+ UserHandle.of(mUserId))) {
+ return allowBackup;
+ }
+
+ if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
+ // Always enable adb backup for SystemBackupAgent in "android" package (this is
+ // done to avoid breaking existing integration tests and might change in the
+ // future).
+ return true;
+ }
- return (app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
+ boolean isPrivileged = (app.flags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ boolean isDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ if (isSystemApp || isPrivileged) {
+ try {
+ return mPackageManager.getProperty(PackageManager.PROPERTY_ALLOW_ADB_BACKUP,
+ packageName).getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Failed to read allowAdbBackup property for + "
+ + packageName);
+ return false;
+ }
+ } else {
+ // All other apps can use adb backup only when running in debuggable mode.
+ return isDebuggable;
+ }
+ case OperationType.BACKUP:
+ return allowBackup;
+ default:
+ Slog.w(TAG, "Unknown operation type:" + mOperationType);
+ return false;
+ }
}
/**
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 6989e320f465..53bfcec11a60 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -949,9 +949,6 @@ public abstract class PackageManagerInternal {
/** Returns whether or not permissions need to be upgraded for the given user */
public abstract boolean isPermissionUpgradeNeeded(@UserIdInt int userId);
- /** Sets the enforcement of reading external storage */
- public abstract void setReadExternalStorageEnforced(boolean enforced);
-
/**
* Allows the integrity component to respond to the
* {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f0677a23765c..ee9d49244d7e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -169,7 +169,6 @@ import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.Xml;
@@ -1973,7 +1972,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void registerNetdEventCallback() {
final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics();
if (ipConnectivityMetrics == null) {
- Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
+ Log.wtf(TAG, "Missing IIpConnectivityMetrics");
return;
}
@@ -2439,7 +2438,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (VDBG || DDBG) log("Setting MTU size: " + iface + ", " + mtu);
mNetd.interfaceSetMtu(iface, mtu);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "exception in interfaceSetMtu()" + e);
+ loge("exception in interfaceSetMtu()" + e);
}
}
@@ -2461,7 +2460,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
try {
- if (VDBG || DDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+ if (VDBG || DDBG) log("Setting tx/rx TCP buffers to " + tcpBufferSizes);
String rmemValues = String.join(" ", values[0], values[1], values[2]);
String wmemValues = String.join(" ", values[3], values[4], values[5]);
@@ -2762,7 +2761,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
if (networkCapabilities.hasConnectivityManagedCapability()) {
- Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
+ Log.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
// Make sure the original object is not mutated. NetworkAgent normally
@@ -3067,7 +3066,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Legacy version of notifyNetworkTestedWithExtras.
// Would only be called if the system has a NetworkStack module older than the
// framework, which does not happen in practice.
- Slog.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken");
+ Log.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken");
}
@Override
@@ -3544,7 +3543,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
numRequests = nai.numForegroundNetworkRequests();
break;
default:
- Slog.wtf(TAG, "Invalid reason. Cannot happen.");
+ Log.wtf(TAG, "Invalid reason. Cannot happen.");
return true;
}
@@ -3706,7 +3705,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mUidToNetworkRequestCount) {
final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
if (requests < 1) {
- Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
+ Log.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
} else if (requests == 1) {
mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
} else {
@@ -3751,7 +3750,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (!nai.networkAgentConfig.explicitlySelected) {
- Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
+ Log.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
}
if (accept != nai.networkAgentConfig.acceptUnvalidated) {
@@ -4021,7 +4020,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
highPriority = nai.networkAgentConfig.explicitlySelected;
break;
default:
- Slog.wtf(TAG, "Unknown notification type " + type);
+ Log.wtf(TAG, "Unknown notification type " + type);
return;
}
@@ -4343,7 +4342,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (this) {
if (!mNetTransitionWakeLock.isHeld()) {
mWakelockLogs.log(String.format("RELEASE: already released (%s)", event));
- Slog.w(TAG, "expected Net Transition WakeLock to be held");
+ Log.w(TAG, "expected Net Transition WakeLock to be held");
return;
}
mNetTransitionWakeLock.release();
@@ -4515,7 +4514,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public void onChange(boolean selfChange) {
- Slog.wtf(TAG, "Should never be reached.");
+ Log.wtf(TAG, "Should never be reached.");
}
@Override
@@ -4530,15 +4529,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private static void log(String s) {
- Slog.d(TAG, s);
+ Log.d(TAG, s);
+ }
+
+ private static void logw(String s) {
+ Log.w(TAG, s);
}
private static void loge(String s) {
- Slog.e(TAG, s);
+ Log.e(TAG, s);
}
private static void loge(String s, Throwable t) {
- Slog.e(TAG, s, t);
+ Log.e(TAG, s, t);
}
/**
@@ -4825,7 +4828,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- Slog.w(TAG, "Lockdown VPN only available to AID_SYSTEM");
+ logw("Lockdown VPN only available to AID_SYSTEM");
return false;
}
@@ -4835,21 +4838,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (mLockdownEnabled) {
byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
if (profileTag == null) {
- Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
+ loge("Lockdown VPN configured but cannot be read from keystore");
return false;
}
String profileName = new String(profileTag);
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
if (profile == null) {
- Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+ loge("Lockdown VPN configured invalid profile " + profileName);
setLockdownTracker(null);
return true;
}
int user = UserHandle.getUserId(Binder.getCallingUid());
Vpn vpn = mVpns.get(user);
if (vpn == null) {
- Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
+ logw("VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
setLockdownTracker(new LockdownVpnTracker(mContext, this, mHandler, vpn, profile));
@@ -4909,7 +4912,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (vpn == null) {
// Shouldn't happen as all code paths that point here should have checked the Vpn
// exists already.
- Slog.wtf(TAG, "User " + userId + " has no Vpn configuration");
+ Log.wtf(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
@@ -4925,7 +4928,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return false;
}
return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
@@ -4946,7 +4949,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return false;
}
if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
@@ -4968,7 +4971,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return null;
}
return vpn.getAlwaysOnPackage();
@@ -4983,7 +4986,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return false;
}
return vpn.getLockdown();
@@ -4998,7 +5001,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return null;
}
return vpn.getLockdownAllowlist();
@@ -5183,7 +5186,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void onPackageReplaced(String packageName, int uid) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
- Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
+ Log.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
return;
}
final int userId = UserHandle.getUserId(uid);
@@ -5194,7 +5197,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
// Legacy always-on VPN won't be affected since the package name is not set.
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
- Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+ log("Restarting always-on VPN package " + packageName + " for user "
+ userId);
vpn.startAlwaysOnVpn(mKeyStore);
}
@@ -5203,7 +5206,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
- Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+ Log.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
return;
}
@@ -5215,7 +5218,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
// Legacy always-on VPN won't be affected since the package name is not set.
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
- Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ log("Removing always-on VPN package " + packageName + " for user "
+ userId);
vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
}
@@ -5831,7 +5834,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Avoid creating duplicates. even if an app makes a direct AIDL call.
// This will never happen if an app calls ConnectivityManager#registerNetworkProvider,
// as that will throw if a duplicate provider is registered.
- Slog.e(TAG, "Attempt to register existing NetworkProviderInfo "
+ loge("Attempt to register existing NetworkProviderInfo "
+ mNetworkProviderInfos.get(npi.messenger).name);
return;
}
@@ -6441,7 +6444,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// stop being matched by the updated agent.
String diff = nai.networkCapabilities.describeImmutableDifferences(nc);
if (!TextUtils.isEmpty(diff)) {
- Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
+ Log.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
}
}
@@ -7001,7 +7004,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
newSatisfier.unlingerRequest(nri.request);
if (!newSatisfier.addRequest(nri.request)) {
- Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ nri.request);
}
} else {
@@ -7349,7 +7352,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
networkAgent.everConnected = true;
if (networkAgent.linkProperties == null) {
- Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
+ Log.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
}
// NetworkCapabilities need to be set before sending the private DNS config to
@@ -8197,8 +8200,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
- if (uid != nri.mUid) {
- if (VDBG) loge("Different uid than registrant attempting to unregister cb");
+ // Caller's UID must either be the registrants (if they are unregistering) or the System's
+ // (if the Binder died)
+ if (uid != nri.mUid && uid != Process.SYSTEM_UID) {
+ if (DBG) loge("Uid(" + uid + ") not registrant's (" + nri.mUid + ") or System's");
return;
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1c99465dfebf..821a967b2b3d 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -24,12 +24,14 @@ import static android.net.INetd.FIREWALL_ALLOWLIST;
import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
import static android.net.INetd.FIREWALL_CHAIN_NONE;
import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.FIREWALL_DENYLIST;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkStats.SET_DEFAULT;
@@ -88,7 +90,6 @@ import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -122,7 +123,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
* Helper class that encapsulates NetworkManagementService dependencies and makes them
* easier to mock in unit tests.
*/
- static class SystemServices {
+ static class Dependencies {
public IBinder getService(String name) {
return ServiceManager.getService(name);
}
@@ -132,6 +133,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
public INetd getNetd() {
return NetdService.get();
}
+
+ public int getCallingUid() {
+ return Binder.getCallingUid();
+ }
}
private static final String TAG = "NetworkManagement";
@@ -157,7 +162,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
private final Handler mDaemonHandler;
- private final SystemServices mServices;
+ private final Dependencies mDeps;
private INetd mNetdService;
@@ -215,6 +220,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
*/
@GuardedBy("mRulesLock")
private SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
+ /**
+ * Contains the per-UID firewall rules that are used when Restricted Networking Mode is enabled.
+ */
+ @GuardedBy("mRulesLock")
+ private SparseIntArray mUidFirewallRestrictedRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mRulesLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
@@ -254,33 +264,32 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
* @param context Binder context for this service
*/
private NetworkManagementService(
- Context context, SystemServices services) {
+ Context context, Dependencies deps) {
mContext = context;
- mServices = services;
+ mDeps = deps;
mDaemonHandler = new Handler(FgThread.get().getLooper());
mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
- mServices.registerLocalService(new LocalService());
+ mDeps.registerLocalService(new LocalService());
synchronized (mTetheringStatsProviders) {
mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd");
}
}
- @VisibleForTesting
- NetworkManagementService() {
+ private NetworkManagementService() {
mContext = null;
mDaemonHandler = null;
- mServices = null;
+ mDeps = null;
mNetdUnsolicitedEventListener = null;
}
- static NetworkManagementService create(Context context, SystemServices services)
+ static NetworkManagementService create(Context context, Dependencies deps)
throws InterruptedException {
final NetworkManagementService service =
- new NetworkManagementService(context, services);
+ new NetworkManagementService(context, deps);
if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
if (DBG) Slog.d(TAG, "Connecting native netd service");
service.connectNativeNetdService();
@@ -289,7 +298,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
public static NetworkManagementService create(Context context) throws InterruptedException {
- return create(context, new SystemServices());
+ return create(context, new Dependencies());
}
public void systemReady() {
@@ -310,7 +319,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return mBatteryStats;
}
mBatteryStats =
- IBatteryStats.Stub.asInterface(mServices.getService(BatteryStats.SERVICE_NAME));
+ IBatteryStats.Stub.asInterface(mDeps.getService(BatteryStats.SERVICE_NAME));
return mBatteryStats;
}
}
@@ -511,7 +520,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
private void connectNativeNetdService() {
- mNetdService = mServices.getNetd();
+ mNetdService = mDeps.getNetd();
try {
mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);
if (DBG) Slog.d(TAG, "Register unsolicited event listener");
@@ -602,9 +611,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
syncFirewallChainLocked(FIREWALL_CHAIN_STANDBY, "standby ");
syncFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, "dozable ");
syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, "powersave ");
+ syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted ");
+
+ final int[] chains = {
+ FIREWALL_CHAIN_STANDBY,
+ FIREWALL_CHAIN_DOZABLE,
+ FIREWALL_CHAIN_POWERSAVE,
+ FIREWALL_CHAIN_RESTRICTED
+ };
- final int[] chains =
- {FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE};
for (int chain : chains) {
if (getFirewallChainState(chain)) {
setFirewallChainEnabled(chain, true);
@@ -1437,7 +1452,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
@Override
public void setUidCleartextNetworkPolicy(int uid, int policy) {
- if (Binder.getCallingUid() != uid) {
+ if (mDeps.getCallingUid() != uid) {
NetworkStack.checkNetworkStackPermission(mContext);
}
@@ -1695,6 +1710,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return FIREWALL_CHAIN_NAME_DOZABLE;
case FIREWALL_CHAIN_POWERSAVE:
return FIREWALL_CHAIN_NAME_POWERSAVE;
+ case FIREWALL_CHAIN_RESTRICTED:
+ return FIREWALL_CHAIN_NAME_RESTRICTED;
default:
throw new IllegalArgumentException("Bad child chain: " + chain);
}
@@ -1708,6 +1725,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_POWERSAVE:
return FIREWALL_ALLOWLIST;
+ case FIREWALL_CHAIN_RESTRICTED:
+ return FIREWALL_ALLOWLIST;
default:
return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST;
}
@@ -1752,6 +1771,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
case FIREWALL_CHAIN_POWERSAVE:
mNetdService.firewallReplaceUidChain("fw_powersave", true, uids);
break;
+ case FIREWALL_CHAIN_RESTRICTED:
+ mNetdService.firewallReplaceUidChain("fw_restricted", true, uids);
+ break;
case FIREWALL_CHAIN_NONE:
default:
Slog.d(TAG, "setFirewallUidRules() called on invalid chain: " + chain);
@@ -1836,6 +1858,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return mUidFirewallDozableRules;
case FIREWALL_CHAIN_POWERSAVE:
return mUidFirewallPowerSaveRules;
+ case FIREWALL_CHAIN_RESTRICTED:
+ return mUidFirewallRestrictedRules;
case FIREWALL_CHAIN_NONE:
return mUidFirewallRules;
default:
@@ -1851,8 +1875,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
return rule;
}
- private static void enforceSystemUid() {
- final int uid = Binder.getCallingUid();
+ private void enforceSystemUid() {
+ final int uid = mDeps.getCallingUid();
if (uid != Process.SYSTEM_UID) {
throw new SecurityException("Only available to AID_SYSTEM");
}
@@ -1910,17 +1934,22 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
synchronized (mRulesLock) {
dumpUidFirewallRule(pw, "", mUidFirewallRules);
- pw.print("UID firewall standby chain enabled: "); pw.println(
- getFirewallChainState(FIREWALL_CHAIN_STANDBY));
+ pw.print("UID firewall standby chain enabled: ");
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_STANDBY));
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules);
- pw.print("UID firewall dozable chain enabled: "); pw.println(
- getFirewallChainState(FIREWALL_CHAIN_DOZABLE));
+ pw.print("UID firewall dozable chain enabled: ");
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_DOZABLE));
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules);
- pw.println("UID firewall powersave chain enabled: " +
- getFirewallChainState(FIREWALL_CHAIN_POWERSAVE));
+ pw.print("UID firewall powersave chain enabled: ");
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_POWERSAVE));
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_POWERSAVE, mUidFirewallPowerSaveRules);
+
+ pw.print("UID firewall restricted mode chain enabled: ");
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_RESTRICTED));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_RESTRICTED,
+ mUidFirewallRestrictedRules);
}
synchronized (mIdleTimerLock) {
@@ -2071,6 +2100,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of power saver mode");
return true;
}
+ if (getFirewallChainState(FIREWALL_CHAIN_RESTRICTED)
+ && mUidFirewallRestrictedRules.get(uid) != FIREWALL_RULE_ALLOW) {
+ if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of restricted mode");
+ return true;
+ }
if (mUidRejectOnMetered.get(uid)) {
if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data"
+ " in the background");
@@ -2096,60 +2130,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
}
- @VisibleForTesting
- class LocalService extends NetworkManagementInternal {
+ private class LocalService extends NetworkManagementInternal {
@Override
public boolean isNetworkRestrictedForUid(int uid) {
return isNetworkRestrictedInternal(uid);
}
}
-
- @VisibleForTesting
- Injector getInjector() {
- return new Injector();
- }
-
- @VisibleForTesting
- class Injector {
- void setDataSaverMode(boolean dataSaverMode) {
- mDataSaverMode = dataSaverMode;
- }
-
- void setFirewallChainState(int chain, boolean state) {
- NetworkManagementService.this.setFirewallChainState(chain, state);
- }
-
- void setFirewallRule(int chain, int uid, int rule) {
- synchronized (mRulesLock) {
- getUidFirewallRulesLR(chain).put(uid, rule);
- }
- }
-
- void setUidOnMeteredNetworkList(boolean denylist, int uid, boolean enable) {
- synchronized (mRulesLock) {
- if (denylist) {
- mUidRejectOnMetered.put(uid, enable);
- } else {
- mUidAllowOnMetered.put(uid, enable);
- }
- }
- }
-
- void reset() {
- synchronized (mRulesLock) {
- setDataSaverMode(false);
- final int[] chains = {
- FIREWALL_CHAIN_DOZABLE,
- FIREWALL_CHAIN_STANDBY,
- FIREWALL_CHAIN_POWERSAVE
- };
- for (int chain : chains) {
- setFirewallChainState(chain, false);
- getUidFirewallRulesLR(chain).clear();
- }
- mUidAllowOnMetered.clear();
- mUidRejectOnMetered.clear();
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 3443918df6ab..fc3a7c855466 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -59,7 +59,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.DumpUtils;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -290,7 +290,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
String useOpenWifiPackage = Global.getString(mContext.getContentResolver(),
Global.USE_OPEN_WIFI_PACKAGE);
if (!TextUtils.isEmpty(useOpenWifiPackage)) {
- LocalServices.getService(PermissionManagerServiceInternal.class)
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
.grantDefaultPermissionsToDefaultUseOpenWifiApp(useOpenWifiPackage,
userId);
}
@@ -302,7 +302,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
false /*notifyForDescendants*/,
mUseOpenWifiPackageObserver);
// Set a callback for the package manager to query the use open wifi app.
- LocalServices.getService(PermissionManagerServiceInternal.class)
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
.setUseOpenWifiAppPackagesProvider((userId) -> {
String useOpenWifiPackage = Global.getString(mContext.getContentResolver(),
Global.USE_OPEN_WIFI_PACKAGE);
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 9bf63cbbb25e..99a1d86d244e 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -52,9 +52,7 @@ import com.android.internal.util.XmlUtils;
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileNotFoundException;
@@ -149,6 +147,7 @@ public class PackageWatchdog {
private static final String ATTR_DURATION = "duration";
private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration";
private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check";
+ private static final String ATTR_MITIGATION_CALLS = "mitigation-calls";
@GuardedBy("PackageWatchdog.class")
private static PackageWatchdog sPackageWatchdog;
@@ -779,7 +778,6 @@ public class PackageWatchdog {
@GuardedBy("mLock")
private Set<String> getPackagesPendingHealthChecksLocked() {
- Slog.d(TAG, "Getting all observed packages pending health checks");
Set<String> packages = new ArraySet<>();
Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
while (oit.hasNext()) {
@@ -828,7 +826,6 @@ public class PackageWatchdog {
Slog.i(TAG, "Cancelling state sync, nothing to sync");
mUptimeAtLastStateSync = 0;
} else {
- Slog.i(TAG, "Scheduling next state sync in " + durationMs + "ms");
mUptimeAtLastStateSync = mSystemClock.uptimeMillis();
mShortTaskHandler.postDelayed(mSyncStateWithScheduledReason, durationMs);
}
@@ -869,7 +866,6 @@ public class PackageWatchdog {
return;
}
- Slog.i(TAG, "Removing " + elapsedMs + "ms from all packages on all observers");
Iterator<ObserverInternal> it = mAllObservers.values().iterator();
while (it.hasNext()) {
ObserverInternal observer = it.next();
@@ -1067,6 +1063,33 @@ public class PackageWatchdog {
}
}
+ /** Convert a {@code LongArrayQueue} to a String of comma-separated values. */
+ public static String longArrayQueueToString(LongArrayQueue queue) {
+ if (queue.size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(queue.get(0));
+ for (int i = 1; i < queue.size(); i++) {
+ sb.append(",");
+ sb.append(queue.get(i));
+ }
+ return sb.toString();
+ }
+ return "";
+ }
+
+ /** Parse a comma-separated String of longs into a LongArrayQueue. */
+ public static LongArrayQueue parseLongArrayQueue(String commaSeparatedValues) {
+ LongArrayQueue result = new LongArrayQueue();
+ if (!TextUtils.isEmpty(commaSeparatedValues)) {
+ String[] values = commaSeparatedValues.split(",");
+ for (String value : values) {
+ result.addLast(Long.parseLong(value));
+ }
+ }
+ return result;
+ }
+
+
/** Dump status of every observer in mAllObservers. */
public void dump(IndentingPrintWriter pw) {
pw.println("Package Watchdog status");
@@ -1240,16 +1263,7 @@ public class PackageWatchdog {
while (XmlUtils.nextElementWithin(parser, innerDepth)) {
if (TAG_PACKAGE.equals(parser.getName())) {
try {
- String packageName = parser.getAttributeValue(
- null, ATTR_NAME);
- long duration = parser.getAttributeLong(
- null, ATTR_DURATION);
- long healthCheckDuration = parser.getAttributeLong(
- null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
- boolean hasPassedHealthCheck = parser.getAttributeBoolean(
- null, ATTR_PASSED_HEALTH_CHECK, false);
- MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName,
- duration, healthCheckDuration, hasPassedHealthCheck);
+ MonitoredPackage pkg = watchdog.parseMonitoredPackage(parser);
if (pkg != null) {
packages.add(pkg);
}
@@ -1305,16 +1319,31 @@ public class PackageWatchdog {
MonitoredPackage newMonitoredPackage(
String name, long durationMs, boolean hasPassedHealthCheck) {
- return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck);
+ return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck,
+ new LongArrayQueue());
}
MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
- boolean hasPassedHealthCheck) {
+ boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) {
VersionedPackage pkg = getVersionedPackage(name);
if (pkg == null) {
return null;
}
- return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs, hasPassedHealthCheck);
+ return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs,
+ hasPassedHealthCheck, mitigationCalls);
+ }
+
+ MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser)
+ throws XmlPullParserException {
+ String packageName = parser.getAttributeValue(null, ATTR_NAME);
+ long duration = parser.getAttributeLong(null, ATTR_DURATION);
+ long healthCheckDuration = parser.getAttributeLong(null,
+ ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
+ boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK);
+ LongArrayQueue mitigationCalls = parseLongArrayQueue(
+ parser.getAttributeValue(null, ATTR_MITIGATION_CALLS));
+ return newMonitoredPackage(packageName,
+ duration, healthCheckDuration, hasPassedHealthCheck, mitigationCalls);
}
/**
@@ -1332,7 +1361,7 @@ public class PackageWatchdog {
// Times when an observer was called to mitigate this package's failure. Sorted in
// ascending order.
@GuardedBy("mLock")
- private final LongArrayQueue mMitigationCalls = new LongArrayQueue();
+ private final LongArrayQueue mMitigationCalls;
// One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after
// methods that could change the health check state: handleElapsedTimeLocked and
// tryPassHealthCheckLocked
@@ -1353,12 +1382,14 @@ public class PackageWatchdog {
@GuardedBy("mLock")
private long mHealthCheckDurationMs = Long.MAX_VALUE;
- private MonitoredPackage(VersionedPackage pkg, long durationMs,
- long healthCheckDurationMs, boolean hasPassedHealthCheck) {
+ MonitoredPackage(VersionedPackage pkg, long durationMs,
+ long healthCheckDurationMs, boolean hasPassedHealthCheck,
+ LongArrayQueue mitigationCalls) {
mPackage = pkg;
mDurationMs = durationMs;
mHealthCheckDurationMs = healthCheckDurationMs;
mHasPassedHealthCheck = hasPassedHealthCheck;
+ mMitigationCalls = mitigationCalls;
updateHealthCheckStateLocked();
}
@@ -1370,6 +1401,8 @@ public class PackageWatchdog {
out.attributeLong(null, ATTR_DURATION, mDurationMs);
out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs);
out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck);
+ LongArrayQueue normalizedCalls = normalizeMitigationCalls();
+ out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls));
out.endTag(null, TAG_PACKAGE);
}
@@ -1423,6 +1456,23 @@ public class PackageWatchdog {
}
/**
+ * Before writing to disk, make the mitigation call timestamps relative to the current
+ * system uptime. This is because they need to be relative to the uptime which will reset
+ * at the next boot.
+ *
+ * @return a LongArrayQueue of the mitigation calls relative to the current system uptime.
+ */
+ @GuardedBy("mLock")
+ public LongArrayQueue normalizeMitigationCalls() {
+ LongArrayQueue normalized = new LongArrayQueue();
+ final long now = mSystemClock.uptimeMillis();
+ for (int i = 0; i < mMitigationCalls.size(); i++) {
+ normalized.addLast(mMitigationCalls.get(i) - now);
+ }
+ return normalized;
+ }
+
+ /**
* Sets the initial health check duration.
*
* @return the new health check state
@@ -1582,6 +1632,16 @@ public class PackageWatchdog {
private long toPositive(long value) {
return value > 0 ? value : Long.MAX_VALUE;
}
+
+ /** Compares the equality of this object with another {@link MonitoredPackage}. */
+ @VisibleForTesting
+ boolean isEqualTo(MonitoredPackage pkg) {
+ return (getName().equals(pkg.getName()))
+ && mDurationMs == pkg.mDurationMs
+ && mHasPassedHealthCheck == pkg.mHasPassedHealthCheck
+ && mHealthCheckDurationMs == pkg.mHealthCheckDurationMs
+ && (mMitigationCalls.toString()).equals(pkg.mMitigationCalls.toString());
+ }
}
/**
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index d30e9fb002e0..51e2b12bcee4 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -158,7 +158,7 @@ public final class SensorPrivacyService extends SystemService {
XmlUtils.beginDocument(parser, XML_TAG_SENSOR_PRIVACY);
parser.next();
String tagName = parser.getName();
- enabled = Boolean.valueOf(parser.getAttributeValue(null, XML_ATTRIBUTE_ENABLED));
+ enabled = parser.getAttributeBoolean(null, XML_ATTRIBUTE_ENABLED, false);
} catch (IOException | XmlPullParserException e) {
Log.e(TAG, "Caught an exception reading the state from storage: ", e);
// Delete the file to prevent the same error on subsequent calls and assume sensor
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index eb55512b4d83..52eb77d7d47d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -80,6 +80,7 @@ import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
@@ -101,10 +102,13 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* Since phone process can be restarted, this class provides a centralized place
@@ -144,14 +148,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
int callerUid;
int callerPid;
- long events;
+ Set<Integer> eventList;
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- boolean matchPhoneStateListenerEvent(long events) {
- return (callback != null) && ((events & this.events) != 0);
+ boolean matchPhoneStateListenerEvent(int event) {
+ return (callback != null) && (this.eventList.contains(event));
}
boolean matchOnSubscriptionsChangedListener() {
@@ -179,7 +183,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ onSubscriptionsChangedListenerCallback
+ " onOpportunisticSubscriptionsChangedListenererCallback="
+ onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
- + " phoneId=" + phoneId + " events=" + Long.toHexString(events) + "}";
+ + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -310,6 +314,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+ private boolean mIsDataEnabled = false;
+
+ private int mDataEnabledReason;
+
/**
* Per-phone map of precise data connection state. The key of the map is the pair of transport
* type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
@@ -319,40 +327,62 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
mPreciseDataConnectionStates;
- // Starting in Q, almost all cellular location requires FINE location enforcement.
- // Prior to Q, cellular was available with COARSE location enforcement. Bits in this
- // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later.
- static final long ENFORCE_LOCATION_PERMISSION_MASK =
- PhoneStateListener.LISTEN_CELL_LOCATION
- | PhoneStateListener.LISTEN_CELL_INFO
- | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
- | PhoneStateListener.LISTEN_BARRING_INFO;
-
- static final long ENFORCE_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
- | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
- | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
- | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
-
- static final long ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_PRECISE_CALL_STATE
- | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
- | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
- | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
- | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
- | PhoneStateListener.LISTEN_BARRING_INFO
- | PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION;
-
- static final long READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
- PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
- | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS;
-
- static final long READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT
- | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED
- | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED
- | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE;
+ private static final Set<Integer> REQUIRE_PRECISE_PHONE_STATE_PERMISSION;
+ static {
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>();
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(
+ PhoneStateListener.EVENT_DATA_ENABLED_CHANGED);
+ }
+
+ private boolean isLocationPermissionRequired(Set<Integer> events) {
+ return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
+ || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED)
+ || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE)
+ || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED);
+ }
+
+ private boolean isPhoneStatePermissionRequired(Set<Integer> events) {
+ return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+ || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
+ || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
+ || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED);
+ }
+
+ private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
+ for (Integer requireEvent : REQUIRE_PRECISE_PHONE_STATE_PERMISSION) {
+ if (events.contains(requireEvent)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) {
+ return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)
+ || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS);
+ }
+
+ private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
+ return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
+ || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
+ || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED);
+ }
private static final int MSG_USER_SWITCHED = 1;
private static final int MSG_UPDATE_DEFAULT_SUB = 2;
@@ -670,7 +700,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.callingFeatureId = callingFeatureId;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
- r.events = 0;
+ r.eventList = new ArraySet<>();
if (DBG) {
log("listen oscl: Register r=" + r);
}
@@ -724,7 +754,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
r.callingFeatureId = callingFeatureId;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
- r.events = 0;
+ r.eventList = new ArraySet<>();
if (DBG) {
log("listen ooscl: Register r=" + r);
}
@@ -797,331 +827,337 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- @Deprecated
- @Override
- public void listen(String callingPackage, IPhoneStateListener callback, int events,
- boolean notifyNow) {
- listenWithFeature(callingPackage, null, callback, events, notifyNow);
- }
-
- @Override
- public void listenWithFeature(String callingPackage, String callingFeatureId,
- IPhoneStateListener callback, long events, boolean notifyNow) {
- listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage,
- callingFeatureId, callback, events, notifyNow);
- }
-
@Override
- public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId,
- IPhoneStateListener callback, long events, boolean notifyNow) {
- listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId);
+ public void listenWithEventList(int subId, String callingPackage, String callingFeatureId,
+ IPhoneStateListener callback, int[] events, boolean notifyNow) {
+ Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet());
+ listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId);
}
private void listen(String callingPackage, @Nullable String callingFeatureId,
- IPhoneStateListener callback, long events, boolean notifyNow, int subId) {
+ IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) {
int callerUserId = UserHandle.getCallingUserId();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
- + " events=0x" + Long.toHexString(events) + " notifyNow=" + notifyNow + " subId="
- + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId;
+ + " events=" + events + " notifyNow=" + notifyNow
+ + " subId=" + subId + " myUserId=" + UserHandle.myUserId()
+ + " callerUserId=" + callerUserId;
mListenLog.log(str);
if (VDBG) {
log(str);
}
- if (events != PhoneStateListener.LISTEN_NONE) {
- // Checks permission and throws SecurityException for disallowed operations. For pre-M
- // apps whose runtime permission has been revoked, we return immediately to skip sending
- // events to the app without crashing it.
- if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
- "listen")) {
- return;
+ if (events.isEmpty()) {
+ if (DBG) {
+ log("listen: Unregister");
}
+ events.clear();
+ remove(callback.asBinder());
+ return;
+ }
- int phoneId = getPhoneIdFromSubId(subId);
- synchronized (mRecords) {
- // register
- IBinder b = callback.asBinder();
- boolean doesLimitApply =
- Binder.getCallingUid() != Process.SYSTEM_UID
- && Binder.getCallingUid() != Process.PHONE_UID
- && Binder.getCallingUid() != Process.myUid();
- Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
+ // Checks permission and throws SecurityException for disallowed operations. For pre-M
+ // apps whose runtime permission has been revoked, we return immediately to skip sending
+ // events to the app without crashing it.
+ if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
+ "listen")) {
+ return;
+ }
- if (r == null) {
- return;
- }
+ int phoneId = getPhoneIdFromSubId(subId);
+ synchronized (mRecords) {
+ // register
+ IBinder b = callback.asBinder();
+ boolean doesLimitApply =
+ Binder.getCallingUid() != Process.SYSTEM_UID
+ && Binder.getCallingUid() != Process.PHONE_UID
+ && Binder.getCallingUid() != Process.myUid();
+ Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
- r.context = mContext;
- r.callback = callback;
- r.callingPackage = callingPackage;
- r.callingFeatureId = callingFeatureId;
- r.callerUid = Binder.getCallingUid();
- r.callerPid = Binder.getCallingPid();
- // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
- // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- } else {//APP specify subID
- r.subId = subId;
- }
- r.phoneId = phoneId;
- r.events = events;
- if (DBG) {
- log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
- }
- if (notifyNow && validatePhoneId(phoneId)) {
- if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
- try {
- if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
- ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
- if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
- r.callback.onServiceStateChanged(rawSs);
- } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
- r.callback.onServiceStateChanged(
- rawSs.createLocationInfoSanitizedCopy(false));
- } else {
- r.callback.onServiceStateChanged(
- rawSs.createLocationInfoSanitizedCopy(true));
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ if (r == null) {
+ return;
+ }
+
+ r.context = mContext;
+ r.callback = callback;
+ r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
+ // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ } else {//APP specify subID
+ r.subId = subId;
+ }
+ r.phoneId = phoneId;
+ r.eventList = events;
+ if (DBG) {
+ log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
+ }
+ if (notifyNow && validatePhoneId(phoneId)) {
+ if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) {
+ try {
+ if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
+ ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(rawSs);
+ } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(
+ rawSs.createLocationInfoSanitizedCopy(false));
+ } else {
+ r.callback.onServiceStateChanged(
+ rawSs.createLocationInfoSanitizedCopy(true));
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
- try {
- if (mSignalStrength[phoneId] != null) {
- int gsmSignalStrength = mSignalStrength[phoneId]
- .getGsmSignalStrength();
- r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
- : gsmSignalStrength));
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ }
+ if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
+ try {
+ if (mSignalStrength[phoneId] != null) {
+ int gsmSignalStrength = mSignalStrength[phoneId]
+ .getGsmSignalStrength();
+ r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+ : gsmSignalStrength));
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
- try {
- r.callback.onMessageWaitingIndicatorChanged(
- mMessageWaiting[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(
+ PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
+ try {
+ r.callback.onMessageWaitingIndicatorChanged(
+ mMessageWaiting[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
- try {
- r.callback.onCallForwardingIndicatorChanged(
- mCallForwarding[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(
+ PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
+ try {
+ r.callback.onCallForwardingIndicatorChanged(
+ mCallForwarding[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
- try {
- if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
- if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
- && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
- // null will be translated to empty CellLocation object in client.
- r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ }
+ if (validateEventAndUserLocked(
+ r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
+ try {
+ if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
+ if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ // null will be translated to empty CellLocation object in client.
+ r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
- try {
- r.callback.onCallStateChanged(mCallState[phoneId],
- getCallIncomingNumber(r, phoneId));
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) {
+ try {
+ r.callback.onCallStateChanged(mCallState[phoneId],
+ getCallIncomingNumber(r, phoneId));
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
- try {
- r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
+ }
+ if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
+ try {
+ r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
mDataConnectionNetworkType[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
- try {
- r.callback.onDataActivity(mDataActivity[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) {
+ try {
+ r.callback.onDataActivity(mDataActivity[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
- try {
- if (mSignalStrength[phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ }
+ if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
+ try {
+ if (mSignalStrength[phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
- != 0) {
- updateReportSignalStrengthDecision(r.subId);
- try {
- if (mSignalStrength[phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ }
+ if (events.contains(
+ PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
+ updateReportSignalStrengthDecision(r.subId);
+ try {
+ if (mSignalStrength[phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
- try {
- if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
- + mCellInfo.get(phoneId));
- if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
- && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ }
+ if (validateEventAndUserLocked(
+ r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
+ try {
+ if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ + mCellInfo.get(phoneId));
+ if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
- try {
- r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) {
+ try {
+ r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
- try {
- r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
- mCallPreciseDisconnectCause[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
+ try {
+ r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
+ mCallPreciseDisconnectCause[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
- try {
- r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
+ try {
+ r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
- try {
- for (PreciseDataConnectionState pdcs
- : mPreciseDataConnectionStates.get(phoneId).values()) {
- r.callback.onPreciseDataConnectionStateChanged(pdcs);
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ }
+ if (events.contains(
+ PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
+ try {
+ for (PreciseDataConnectionState pdcs
+ : mPreciseDataConnectionStates.get(phoneId).values()) {
+ r.callback.onPreciseDataConnectionStateChanged(pdcs);
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) {
- try {
- r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) {
+ try {
+ r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) {
- try {
- r.callback.onVoiceActivationStateChanged(
- mVoiceActivationState[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
+ try {
+ r.callback.onVoiceActivationStateChanged(
+ mVoiceActivationState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) {
- try {
- r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
+ try {
+ r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
- try {
- r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
+ try {
+ r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
- try {
- if (mTelephonyDisplayInfos[phoneId] != null) {
- r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
- }
- } catch (RemoteException ex) {
- remove(r.binder);
+ }
+ if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
+ try {
+ if (mTelephonyDisplayInfos[phoneId] != null) {
+ r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
}
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
- try {
- r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
+ try {
+ r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
- try {
- r.callback.onPhoneCapabilityChanged(mPhoneCapability);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
+ try {
+ r.callback.onPhoneCapabilityChanged(mPhoneCapability);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener
- .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
- try {
- r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(
+ PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
+ try {
+ r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
- try {
- r.callback.onRadioPowerStateChanged(mRadioPowerState);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) {
+ try {
+ r.callback.onRadioPowerStateChanged(mRadioPowerState);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) {
- try {
- r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) {
+ try {
+ r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
- try {
- r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) {
+ try {
+ r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_BARRING_INFO) != 0) {
- BarringInfo barringInfo = mBarringInfo.get(phoneId);
- BarringInfo biNoLocation = barringInfo != null
- ? barringInfo.createLocationInfoSanitizedCopy() : null;
- if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
- try {
- r.callback.onBarringInfoChanged(
- checkFineLocationAccess(r, Build.VERSION_CODES.BASE)
- ? barringInfo : biNoLocation);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) {
+ BarringInfo barringInfo = mBarringInfo.get(phoneId);
+ BarringInfo biNoLocation = barringInfo != null
+ ? barringInfo.createLocationInfoSanitizedCopy() : null;
+ if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
+ try {
+ r.callback.onBarringInfoChanged(
+ checkFineLocationAccess(r, Build.VERSION_CODES.BASE)
+ ? barringInfo : biNoLocation);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
- if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) {
- try {
- r.callback.onPhysicalChannelConfigurationChanged(
- mPhysicalChannelConfigs);
- } catch (RemoteException ex) {
- remove(r.binder);
- }
+ }
+ if (events.contains(
+ PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) {
+ try {
+ r.callback.onPhysicalChannelConfigChanged(
+ mPhysicalChannelConfigs);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ if (events.contains(
+ PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) {
+ try {
+ r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason);
+ } catch (RemoteException ex) {
+ remove(r.binder);
}
}
}
- } else {
- if(DBG) log("listen: Unregister");
- remove(callback.asBinder());
}
}
@@ -1133,7 +1169,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
// If any of the system clients wants to always listen to signal strength,
// we need to set it on.
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) {
+ PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
telephonyManager.createForSubscriptionId(subscriptionId)
.setAlwaysReportSignalStrength(true);
return;
@@ -1234,7 +1270,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
// strength is removed from registry records, we need to check if
// the signal strength decision needs to update on its slot.
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) {
+ PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
updateReportSignalStrengthDecision(r.subId);
}
return;
@@ -1254,8 +1290,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
- (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
+ if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
+ && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
// Ensure the listener has read call log permission; if they do not return
// an empty phone number.
@@ -1289,9 +1325,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCallState[phoneId] = state;
mCallIncomingNumber[phoneId] = incomingNumber;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
- (r.subId == subId) &&
- (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
+ if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED)
+ && (r.subId == subId)
+ && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
try {
String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId);
r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
@@ -1331,8 +1367,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " state=" + state);
}
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
- idMatch(r.subId, subId, phoneId)) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
ServiceState stateToSend;
@@ -1393,7 +1430,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
&& r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE)
+ PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
if (DBG) {
log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
@@ -1404,7 +1441,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if ((activationType == SIM_ACTIVATION_TYPE_DATA)
&& r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE)
+ PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
if (DBG) {
log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
@@ -1443,9 +1480,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
- if ((r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)
+ if ((r.matchPhoneStateListenerEvent(
+ PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
|| r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH))
+ PhoneStateListener.
+ EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
@@ -1458,8 +1497,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mRemoveList.add(r.binder);
}
}
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) &&
- idMatch(r.subId, subId, phoneId)) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -1505,8 +1545,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
- idMatch(r.subId, subId, phoneId)) {
+ PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCarrierNetworkChange(active);
} catch (RemoteException ex) {
@@ -1536,9 +1576,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mCellInfo.set(phoneId, cellInfo);
for (Record r : mRecords) {
- if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
- idMatch(r.subId, subId, phoneId) &&
- (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ if (validateEventAndUserLocked(
+ r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)
+ && idMatch(r.subId, subId, phoneId)
+ && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
if (DBG_LOC) {
@@ -1570,8 +1611,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mMessageWaiting[phoneId] = mwi;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) &&
- idMatch(r.subId, subId, phoneId)) {
+ PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onMessageWaitingIndicatorChanged(mwi);
} catch (RemoteException ex) {
@@ -1597,8 +1638,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mUserMobileDataState[phoneId] = state;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) &&
- idMatch(r.subId, subId, phoneId)) {
+ PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onUserMobileDataStateChanged(state);
} catch (RemoteException ex) {
@@ -1636,7 +1677,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
+ PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)
&& idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
try {
r.callback.onDisplayInfoChanged(telephonyDisplayInfo);
@@ -1668,8 +1709,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mCallForwarding[phoneId] = cfi;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) &&
- idMatch(r.subId, subId, phoneId)) {
+ PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallForwardingIndicatorChanged(cfi);
} catch (RemoteException ex) {
@@ -1696,8 +1737,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mDataActivity[phoneId] = state;
for (Record r : mRecords) {
// Notify by correct subId.
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY) &&
- idMatch(r.subId, subId, phoneId)) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onDataActivity(state);
} catch (RemoteException ex) {
@@ -1744,7 +1786,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mLocalLog.log(str);
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_DATA_CONNECTION_STATE)
+ PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
@@ -1769,7 +1811,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (!Objects.equals(oldState, preciseState)) {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
+ PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onPreciseDataConnectionStateChanged(preciseState);
@@ -1814,9 +1856,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) {
mCellIdentity[phoneId] = cellIdentity;
for (Record r : mRecords) {
- if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
- idMatch(r.subId, subId, phoneId) &&
- (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
+ if (validateEventAndUserLocked(
+ r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)
+ && idMatch(r.subId, subId, phoneId)
+ && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
if (DBG_LOC) {
@@ -1867,7 +1910,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
@@ -1876,7 +1920,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
if (notifyCallAttributes && r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED)
+ PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -1925,7 +1969,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mImsReasonInfo.set(phoneId, imsReasonInfo);
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES)
+ PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
@@ -1957,8 +2001,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mSrvccState[phoneId] = state;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) &&
- idMatch(r.subId, subId, phoneId)) {
+ PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)
+ && idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r);
@@ -1986,7 +2030,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
}
if ((r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT))
+ PhoneStateListener.EVENT_OEM_HOOK_RAW))
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onOemHookRawEvent(rawData);
@@ -2014,7 +2058,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE)) {
+ PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) {
try {
r.callback.onPhoneCapabilityChanged(capability);
} catch (RemoteException ex) {
@@ -2039,7 +2083,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) {
+ PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) {
try {
r.callback.onActiveDataSubIdChanged(activeDataSubId);
} catch (RemoteException ex) {
@@ -2066,7 +2110,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED)
+ PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onRadioPowerStateChanged(state);
@@ -2095,7 +2139,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST)
+ PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -2127,7 +2171,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
// Send to all listeners regardless of subscription
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)) {
+ PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) {
try {
r.callback.onOutgoingEmergencyCall(emergencyNumber, subId);
} catch (RemoteException ex) {
@@ -2151,7 +2195,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
// Send to all listeners regardless of subscription
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)) {
+ PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) {
try {
r.callback.onOutgoingEmergencySms(emergencyNumber, subId);
} catch (RemoteException ex) {
@@ -2181,7 +2225,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED)
+ PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
@@ -2212,7 +2256,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_REGISTRATION_FAILURE)
+ PhoneStateListener.EVENT_REGISTRATION_FAILURE)
&& idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onRegistrationFailed(
@@ -2255,7 +2299,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_BARRING_INFO)
+ PhoneStateListener.EVENT_BARRING_INFO_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
@@ -2282,14 +2326,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
* @param subId the subId
* @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
*/
- public void notifyPhysicalChannelConfigurationForSubscriber(
+ public void notifyPhysicalChannelConfigForSubscriber(
int subId, List<PhysicalChannelConfig> configs) {
- if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
+ if (!checkNotifyPermission("notifyPhysicalChannelConfig()")) {
return;
}
if (VDBG) {
- log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs);
+ log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs);
}
synchronized (mRecords) {
@@ -2298,15 +2342,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId));
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION)
+ PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
&& idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
- log("notifyPhysicalChannelConfiguration: "
+ log("notifyPhysicalChannelConfig: "
+ "mPhysicalChannelConfigs="
+ configs + " r=" + r);
}
- r.callback.onPhysicalChannelConfigurationChanged(configs);
+ r.callback.onPhysicalChannelConfigChanged(configs);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -2616,18 +2660,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
== PackageManager.PERMISSION_GRANTED;
}
- private boolean checkListenerPermission(long events, int subId, String callingPackage,
- @Nullable String callingFeatureId, String message) {
+ private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage,
+ @Nullable String callingFeatureId, String message) {
LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
new LocationAccessPolicy.LocationPermissionQuery.Builder()
- .setCallingPackage(callingPackage)
- .setMethod(message + " events: " + events)
- .setCallingPid(Binder.getCallingPid())
- .setCallingUid(Binder.getCallingUid());
+ .setCallingPackage(callingPackage)
+ .setMethod(message + " events: " + events)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid());
boolean shouldCheckLocationPermissions = false;
- if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) {
+ if (isLocationPermissionRequired(events)) {
// Everything that requires fine location started in Q. So far...
locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
// If we're enforcing fine starting in Q, we also want to enforce coarse even for
@@ -2652,14 +2696,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
+ if (isPhoneStatePermissionRequired(events)) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mContext, subId, callingPackage, callingFeatureId, message)) {
isPermissionCheckSuccessful = false;
}
}
- if ((events & ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
+ if (isPrecisePhoneStatePermissionRequired(events)) {
// check if calling app has either permission READ_PRECISE_PHONE_STATE
// or with carrier privileges
try {
@@ -2670,21 +2714,20 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) {
+ if (isActiveEmergencySessionPermissionRequired(events)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
}
- if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
+ if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
}
- if ((events & READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK) != 0) {
+ if (isPrivilegedPhoneStatePermissionRequired(events)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
}
-
return isPermissionCheckSuccessful;
}
@@ -2692,25 +2735,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
int size = mRemoveList.size();
if (VDBG) log("handleRemoveListLocked: mRemoveList.size()=" + size);
if (size > 0) {
- for (IBinder b: mRemoveList) {
+ for (IBinder b : mRemoveList) {
remove(b);
}
mRemoveList.clear();
}
}
- private boolean validateEventsAndUserLocked(Record r, int events) {
+ private boolean validateEventAndUserLocked(Record r, int event) {
int foregroundUser;
final long callingIdentity = Binder.clearCallingIdentity();
boolean valid = false;
try {
foregroundUser = ActivityManager.getCurrentUser();
valid = UserHandle.getUserId(r.callerUid) == foregroundUser
- && r.matchPhoneStateListenerEvent(events);
+ && r.matchPhoneStateListenerEvent(event);
if (DBG | DBG_LOC) {
- log("validateEventsAndUserLocked: valid=" + valid
+ log("validateEventAndUserLocked: valid=" + valid
+ " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
- + " r.events=" + r.events + " events=" + events);
+ + " r.eventList=" + r.eventList + " event=" + event);
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
@@ -2822,9 +2865,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
private void checkPossibleMissNotify(Record r, int phoneId) {
- long events = r.events;
+ Set<Integer> events = r.eventList;
+
+ if (events == null || events.isEmpty()) {
+ log("checkPossibleMissNotify: events = null.");
+ return;
+ }
- if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
+ if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) {
try {
if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" +
mServiceState[phoneId]);
@@ -2843,8 +2891,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0
- || (events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
+ if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)
+ || events.contains(
+ PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
SignalStrength signalStrength = mSignalStrength[phoneId];
@@ -2859,7 +2908,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
+ if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) {
try {
if (mSignalStrength[phoneId] != null) {
int gsmSignalStrength = mSignalStrength[phoneId]
@@ -2876,7 +2925,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
+ if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) {
try {
if (DBG_LOC) {
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
@@ -2891,7 +2940,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) {
+ if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId="
@@ -2903,7 +2952,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) {
+ if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onDisplayInfoChanged phoneId="
@@ -2917,7 +2966,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
+ if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId="
@@ -2930,7 +2979,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
+ if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
try {
if (VDBG) {
log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId="
@@ -2943,7 +2992,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
+ if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) {
try {
if (DBG_LOC) {
log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = "
@@ -2959,7 +3008,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
+ if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
try {
if (DBG) {
log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState"
diff --git a/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java b/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java
index b9453db8eb0a..725bccf76bb9 100644
--- a/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java
+++ b/services/core/java/com/android/server/accounts/AccountAuthenticatorCache.java
@@ -27,10 +27,10 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
@@ -83,13 +83,13 @@ import java.io.IOException;
private static class MySerializer implements XmlSerializerAndParser<AuthenticatorDescription> {
@Override
- public void writeAsXml(AuthenticatorDescription item, XmlSerializer out)
+ public void writeAsXml(AuthenticatorDescription item, TypedXmlSerializer out)
throws IOException {
out.attribute(null, "type", item.type);
}
@Override
- public AuthenticatorDescription createFromXml(XmlPullParser parser)
+ public AuthenticatorDescription createFromXml(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
return AuthenticatorDescription.newKey(parser.getAttributeValue(null, "type"));
}
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index d99b195351f1..1f35b88c8cbd 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -71,7 +71,6 @@ import android.util.Xml;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
@@ -79,7 +78,6 @@ import com.android.server.FgThread;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedReader;
import java.io.File;
@@ -89,7 +87,6 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.AbstractMap;
@@ -1757,21 +1754,21 @@ public class AdbDebuggingManager {
dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
} catch (IOException e) {
- Slog.e(TAG, "Cannot read user keys", e);
+ Slog.i(TAG, "Cannot read user keys", e);
}
try {
dump.write("system_keys", AdbDebuggingManagerProto.SYSTEM_KEYS,
FileUtils.readTextFile(new File("/adb_keys"), 0, null));
} catch (IOException e) {
- Slog.e(TAG, "Cannot read system keys", e);
+ Slog.i(TAG, "Cannot read system keys", e);
}
try {
dump.write("keystore", AdbDebuggingManagerProto.KEYSTORE,
FileUtils.readTextFile(getAdbTempKeysFile(), 0, null));
} catch (IOException e) {
- Slog.e(TAG, "Cannot read keystore: ", e);
+ Slog.i(TAG, "Cannot read keystore: ", e);
}
dump.end(token);
@@ -1966,9 +1963,9 @@ public class AdbDebuggingManager {
String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
long connectionTime;
try {
- connectionTime = Long.valueOf(
- parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
- } catch (NumberFormatException e) {
+ connectionTime = parser.getAttributeLong(null,
+ XML_ATTRIBUTE_LAST_CONNECTION);
+ } catch (XmlPullParserException e) {
Slog.e(TAG,
"Caught a NumberFormatException parsing the last connection time: "
+ e);
@@ -2020,9 +2017,9 @@ public class AdbDebuggingManager {
String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
long connectionTime;
try {
- connectionTime = Long.valueOf(
- parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
- } catch (NumberFormatException e) {
+ connectionTime = parser.getAttributeLong(null,
+ XML_ATTRIBUTE_LAST_CONNECTION);
+ } catch (XmlPullParserException e) {
Slog.e(TAG,
"Caught a NumberFormatException parsing the last connection time: "
+ e);
@@ -2150,8 +2147,8 @@ public class AdbDebuggingManager {
for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
serializer.startTag(null, XML_TAG_ADB_KEY);
serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
- serializer.attribute(null, XML_ATTRIBUTE_LAST_CONNECTION,
- String.valueOf(keyEntry.getValue()));
+ serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CONNECTION,
+ keyEntry.getValue());
serializer.endTag(null, XML_TAG_ADB_KEY);
}
for (String bssid : mTrustedNetworks) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index da6e7ff84525..bcd122d33f7d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -139,6 +139,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Activity;
+import android.app.ActivityClient;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManagerInternal;
@@ -264,7 +265,6 @@ import android.os.UserManager;
import android.os.WorkSource;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
-import android.permission.PermissionManagerInternal.CheckPermissionDelegate;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.server.ServerProtoEnums;
@@ -288,7 +288,6 @@ import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
-import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -326,7 +325,6 @@ import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.QuadFunction;
-import com.android.internal.util.function.TriFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleInternal;
@@ -394,7 +392,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.function.BiFunction;
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -2859,12 +2856,13 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
int finishTask) {
- return mActivityTaskManager.finishActivity(token, resultCode, resultData, finishTask);
+ return ActivityClient.getInstance().finishActivity(token, resultCode, resultData,
+ finishTask);
}
@Override
public void setRequestedOrientation(IBinder token, int requestedOrientation) {
- mActivityTaskManager.setRequestedOrientation(token, requestedOrientation);
+ ActivityClient.getInstance().setRequestedOrientation(token, requestedOrientation);
}
@Override
@@ -5711,7 +5709,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
@Override
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
- return mActivityTaskManager.moveActivityTaskToBack(token, nonRoot);
+ return ActivityClient.getInstance().moveActivityTaskToBack(token, nonRoot);
}
@Override
@@ -5746,7 +5744,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public int getTaskForActivity(IBinder token, boolean onlyRoot) {
- return mActivityTaskManager.getTaskForActivity(token, onlyRoot);
+ return ActivityClient.getInstance().getTaskForActivity(token, onlyRoot);
}
@Override
@@ -6719,7 +6717,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public boolean isTopOfTask(IBinder token) {
- return mActivityTaskManager.isTopOfTask(token);
+ return ActivityClient.getInstance().isTopOfTask(token);
}
@Override
@@ -14485,7 +14483,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
app.info.packageName, AppOpsManager.MODE_ERRORED);
mAppOpsService.setAppOpsServiceDelegate(null);
- getPermissionManagerInternalLocked().setCheckPermissionDelegate(null);
+ getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation();
mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
instr.mUiAutomationConnection).sendToTarget();
}
@@ -16305,14 +16303,9 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public ActivityPresentationInfo getActivityPresentationInfo(IBinder token) {
- int displayId = Display.INVALID_DISPLAY;
- try {
- displayId = mActivityTaskManager.getDisplayId(token);
- } catch (RemoteException e) {
- }
-
- return new ActivityPresentationInfo(mActivityTaskManager.getTaskForActivity(token,
- /*onlyRoot=*/ false), displayId,
+ final ActivityClient ac = ActivityClient.getInstance();
+ return new ActivityPresentationInfo(ac.getTaskForActivity(token,
+ /*onlyRoot=*/ false), ac.getDisplayId(token),
mActivityTaskManager.getActivityClassForToken(token));
}
@@ -17205,12 +17198,6 @@ public class ActivityManagerService extends IActivityManager.Stub
// We allow delegation only to one instrumentation started from the shell
synchronized (ActivityManagerService.this) {
- // If there is a delegate it should be the same instance for app ops and permissions.
- if (mAppOpsService.getAppOpsServiceDelegate()
- != getPermissionManagerInternalLocked().getCheckPermissionDelegate()) {
- throw new IllegalStateException("Bad shell delegate state");
- }
-
// If the delegate is already set up for the target UID, nothing to do.
if (mAppOpsService.getAppOpsServiceDelegate() != null) {
if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) {
@@ -17239,10 +17226,14 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// Hook them up...
- final ShellDelegate shellDelegate = new ShellDelegate(
- instr.mTargetInfo.packageName, delegateUid, permissions);
+ final ShellDelegate shellDelegate = new ShellDelegate(delegateUid,
+ permissions);
mAppOpsService.setAppOpsServiceDelegate(shellDelegate);
- getPermissionManagerInternalLocked().setCheckPermissionDelegate(shellDelegate);
+ final String packageName = instr.mTargetInfo.packageName;
+ final List<String> permissionNames = permissions != null ?
+ Arrays.asList(permissions) : null;
+ getPermissionManagerInternalLocked().startShellPermissionIdentityDelegation(
+ delegateUid, packageName, permissionNames);
return;
}
}
@@ -17256,17 +17247,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
synchronized (ActivityManagerService.this) {
mAppOpsService.setAppOpsServiceDelegate(null);
- getPermissionManagerInternalLocked().setCheckPermissionDelegate(null);
+ getPermissionManagerInternalLocked().stopShellPermissionIdentityDelegation();
}
}
- private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate {
- private final String mTargetPackageName;
+ private class ShellDelegate implements CheckOpsDelegate {
private final int mTargetUid;
private @Nullable String[] mPermissions;
- ShellDelegate(String targetPackageName, int targetUid, @Nullable String[] permissions) {
- mTargetPackageName = targetPackageName;
+ ShellDelegate(int targetUid, @Nullable String[] permissions) {
mTargetUid = targetUid;
mPermissions = permissions;
}
@@ -17329,34 +17318,6 @@ public class ActivityManagerService extends IActivityManager.Stub
message, shouldCollectMessage);
}
- @Override
- public int checkPermission(String permName, String pkgName, int userId,
- TriFunction<String, String, Integer, Integer> superImpl) {
- if (mTargetPackageName.equals(pkgName) && isTargetPermission(permName)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return superImpl.apply(permName, "com.android.shell", userId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- return superImpl.apply(permName, pkgName, userId);
- }
-
- @Override
- public int checkUidPermission(String permName, int uid,
- BiFunction<String, Integer, Integer> superImpl) {
- if (uid == mTargetUid && isTargetPermission(permName)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return superImpl.apply(permName, Process.SHELL_UID);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- return superImpl.apply(permName, uid);
- }
-
private boolean isTargetOp(int code) {
// null permissions means all ops are targeted
if (mPermissions == null) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index fdc0f5949da8..ca20224ce315 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -353,7 +353,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
mUidsToRemove.clear();
mCurrentFuture = null;
mUseLatestStates = true;
- if ((updateFlags & UPDATE_ALL) != 0) {
+ if (updateFlags == UPDATE_ALL) {
cancelSyncDueToBatteryLevelChangeLocked();
}
if ((updateFlags & UPDATE_CPU) != 0) {
@@ -533,7 +533,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff);
}
- if ((updateFlags & UPDATE_ALL) != 0) {
+ if (updateFlags == UPDATE_ALL) {
mStats.updateKernelWakelocksLocked(elapsedRealtimeUs);
mStats.updateKernelMemoryBandwidthLocked(elapsedRealtimeUs);
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 46e16bcbb1b6..3b6f0ac42ed2 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -25,6 +25,7 @@ import android.hardware.power.stats.EnergyConsumerId;
import android.hardware.power.stats.EnergyConsumerResult;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
+import android.os.BatteryUsageStats;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -57,6 +58,7 @@ import android.util.Slog;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.BatteryUsageStatsProvider;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
@@ -105,6 +107,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
private final BatteryExternalStatsWorker mWorker;
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private native void getLowPowerStats(RpmStats rpmStats);
private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
@@ -258,6 +261,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
mStats.setPowerProfileLocked(new PowerProfile(context));
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
}
public void publish() {
@@ -550,6 +554,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// Public interface...
+ /**
+ * Returns BatteryUsageStats, which contains power attribution data on a per-subsystem
+ * and per-UID basis.
+ */
+ public BatteryUsageStats getBatteryUsageStats() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ return mBatteryUsageStatsProvider.getBatteryUsageStats();
+ }
+
public byte[] getStatistics() {
mContext.enforceCallingPermission(
android.Manifest.permission.BATTERY_STATS, null);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 89150aee44ad..4d971a51e885 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -545,7 +545,6 @@ public class AudioService extends IAudioService.Stub
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
AudioSystem.DEVICE_OUT_HDMI_ARC,
- AudioSystem.DEVICE_OUT_SPDIF,
AudioSystem.DEVICE_OUT_AUX_LINE));
// Devices for which the volume is always max, no volume panel
Set<Integer> mFullVolumeDevices = new HashSet<>();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 0194259d6289..75e1938656e3 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
@@ -809,12 +810,13 @@ public class BiometricService extends SystemService {
public List<FingerprintSensorPropertiesInternal> getFingerprintSensorProperties(
Context context) {
- final FingerprintManager fpm = context.getSystemService(FingerprintManager.class);
- if (fpm != null) {
- return fpm.getSensorPropertiesInternal();
- } else {
- return new ArrayList<>();
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ final FingerprintManager fpm = context.getSystemService(FingerprintManager.class);
+ if (fpm != null) {
+ return fpm.getSensorPropertiesInternal();
+ }
}
+ return new ArrayList<>();
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 62c9295adda5..f07bf1e236b8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -53,7 +53,7 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
@Override
public byte[] dumpSensorServiceStateProto() throws RemoteException {
- return mFaceService.dumpSensorServiceStateProto();
+ return mFaceService.dumpSensorServiceStateProto(mSensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 47897a1a0588..cb56e8cd4b7f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -127,17 +127,6 @@ public class FaceService extends SystemService implements BiometricServiceCallba
return properties;
}
- @NonNull
- private List<Face> getEnrolledFaces(int userId, String opPackageName) {
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
- if (provider == null) {
- Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName);
- return Collections.emptyList();
- }
-
- return provider.second.getEnrolledFaces(provider.first, userId);
- }
-
/**
* Receives the incoming binder calls from FaceManager.
*/
@@ -157,14 +146,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba
}
@Override
- public byte[] dumpSensorServiceStateProto() {
+ public byte[] dumpSensorServiceStateProto(int sensorId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ProtoOutputStream proto = new ProtoOutputStream();
- for (ServiceProvider provider : mServiceProviders) {
- for (FaceSensorPropertiesInternal props : provider.getSensorProperties()) {
- provider.dumpProtoState(props.sensorId, proto);
- }
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider != null) {
+ provider.dumpProtoState(sensorId, proto);
}
proto.flush();
return proto.getBytes();
@@ -439,6 +427,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba
pw.println("Dumping for sensorId: " + props.sensorId
+ ", provider: " + provider.getClass().getSimpleName());
provider.dumpInternal(props.sensorId, pw);
+ pw.println();
}
}
}
@@ -472,7 +461,13 @@ public class FaceService extends SystemService implements BiometricServiceCallba
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- return FaceService.this.getEnrolledFaces(userId, opPackageName);
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for getEnrolledFaces, caller: " + opPackageName);
+ return Collections.emptyList();
+ }
+
+ return provider.getEnrolledFaces(sensorId, userId);
}
@Override // Binder call
@@ -483,7 +478,16 @@ public class FaceService extends SystemService implements BiometricServiceCallba
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- return !FaceService.this.getEnrolledFaces(userId, opPackageName).isEmpty();
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for hasEnrolledFaces, caller: " + opPackageName);
+ return false;
+ }
+
+ final boolean enrolled = provider.getEnrolledFaces(sensorId, userId).size() > 0;
+ Slog.d(TAG, "hasEnrolledFaces, sensor: " + sensorId + ", enrolled: " + enrolled);
+
+ return provider.getEnrolledFaces(sensorId, userId).size() > 0;
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
index a0ffe58c779b..a0cd4a56ea12 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
@@ -72,13 +72,13 @@ public class FaceUtils implements BiometricUtils<Face> {
}
/**
- * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses,
- * which do not support a well defined sensorId from the HAL.
+ * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses.
+ * Framework-side cache is always stored in the same file, regardless of sensorId.
*/
- public static FaceUtils getInstance() {
+ public static FaceUtils getLegacyInstance(int sensorId) {
// Note that sensorId for legacy services can be hard-coded to 0 since it's only used
// to index into the sensor states map.
- return getInstance(0 /* sensorId */, LEGACY_FACE_FILE);
+ return getInstance(sensorId, LEGACY_FACE_FILE);
}
private FaceUtils(String fileName) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index 4c983fb5fa6c..9ed8f789aaa2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -153,7 +153,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
// Fake authentication with any of the existing fingers
- List<Face> faces = FaceUtils.getInstance().getBiometricsForUser(mContext, userId);
+ List<Face> faces = FaceUtils.getLegacyInstance(mSensorId)
+ .getBiometricsForUser(mContext, userId);
if (faces.isEmpty()) {
Slog.w(TAG, "No faces, returning");
return;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index d384bc645d61..c4e4d1fe0f82 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -166,7 +166,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@Override
public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) {
mHandler.post(() -> {
- final CharSequence name = FaceUtils.getInstance()
+ final CharSequence name = FaceUtils.getLegacyInstance(mSensorId)
.getUniqueName(mContext, userId);
final Face face = new Face(name, faceId, deviceId);
@@ -471,7 +471,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
@Override
@NonNull
public List<Face> getEnrolledFaces(int sensorId, int userId) {
- return FaceUtils.getInstance().getBiometricsForUser(mContext, userId);
+ return FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId);
}
@Override
@@ -610,8 +610,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FaceUtils.getInstance(), disabledFeatures, ENROLL_TIMEOUT_SEC,
- surfaceHandle, mSensorId);
+ opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+ ENROLL_TIMEOUT_SEC, surfaceHandle, mSensorId);
mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
@Override
@@ -665,7 +665,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
- FaceUtils.getInstance(), mSensorId, mAuthenticatorIds);
+ FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -748,7 +748,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId);
final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList,
- FaceUtils.getInstance(), mAuthenticatorIds);
+ FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -777,7 +777,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final long userToken = proto.start(SensorStateProto.USER_STATES);
proto.write(UserStateProto.USER_ID, userId);
- proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getInstance()
+ proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getLegacyInstance(mSensorId)
.getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
@@ -801,7 +801,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
JSONArray sets = new JSONArray();
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
- final int c = FaceUtils.getInstance().getBiometricsForUser(mContext, userId).size();
+ final int c = FaceUtils.getLegacyInstance(mSensorId)
+ .getBiometricsForUser(mContext, userId).size();
JSONObject set = new JSONObject();
set.put("id", userId);
set.put("count", c);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 1616457e09a9..d4cdc8b18898 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -54,7 +54,7 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
@Override
public byte[] dumpSensorServiceStateProto() throws RemoteException {
- return mFingerprintService.dumpSensorServiceStateProto();
+ return mFingerprintService.dumpSensorServiceStateProto(mSensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 6e91c9a61d42..61f9cc40d233 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -115,15 +115,13 @@ public class FingerprintService extends SystemService implements BiometricServic
}
@Override
- public byte[] dumpSensorServiceStateProto() {
+ public byte[] dumpSensorServiceStateProto(int sensorId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ProtoOutputStream proto = new ProtoOutputStream();
- for (ServiceProvider provider : mServiceProviders) {
- for (FingerprintSensorPropertiesInternal props
- : provider.getSensorProperties()) {
- provider.dumpProtoState(props.sensorId, proto);
- }
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider != null) {
+ provider.dumpProtoState(sensorId, proto);
}
proto.flush();
return proto.getBytes();
@@ -437,6 +435,7 @@ public class FingerprintService extends SystemService implements BiometricServic
pw.println("Dumping for sensorId: " + props.sensorId
+ ", provider: " + provider.getClass().getSimpleName());
provider.dumpInternal(props.sensorId, pw);
+ pw.println();
}
}
}
@@ -588,13 +587,13 @@ public class FingerprintService extends SystemService implements BiometricServic
@Nullable byte [] hardwareAuthToken, String opPackageName) {
Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT);
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final ServiceProvider provider = getProviderForSensor(sensorId);
if (provider == null) {
Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName);
return;
}
- provider.second.scheduleResetLockout(sensorId, userId, hardwareAuthToken);
+ provider.scheduleResetLockout(sensorId, userId, hardwareAuthToken);
}
@Override
@@ -747,14 +746,14 @@ public class FingerprintService extends SystemService implements BiometricServic
}
/**
- * For devices with only a single provider, returns that provider. If no providers, or multiple
- * providers exist, returns null.
+ * For devices with only a single provider, returns that provider. If multiple providers,
+ * returns the first one. If no providers, returns null.
*/
@Nullable
private Pair<Integer, ServiceProvider> getSingleProvider() {
final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties();
- if (properties.size() != 1) {
- Slog.e(TAG, "Multiple sensors found: " + properties.size());
+ if (properties.isEmpty()) {
+ Slog.e(TAG, "No providers found");
return null;
}
@@ -767,7 +766,7 @@ public class FingerprintService extends SystemService implements BiometricServic
}
}
- Slog.e(TAG, "Single sensor, but provider not found");
+ Slog.e(TAG, "Provider not found");
return null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index 6da86502b64c..b3d2419901e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -75,12 +75,12 @@ public class FingerprintUtils implements BiometricUtils<Fingerprint> {
/**
* Legacy getter for {@link android.hardware.biometrics.fingerprint.V2_1} ands its extended
- * subclasses, which do not support a well defined sensorId from the HAL.
+ * subclasses. Framework-side cache is always stored in the same file, regardless of sensorId.
*/
- public static FingerprintUtils getInstance() {
+ public static FingerprintUtils getLegacyInstance(int sensorId) {
// Note that sensorId for legacy services can be hard-coded to 0 since it's only used
// to index into the sensor states map.
- return getInstance(0 /* sensorId */, LEGACY_FINGERPRINT_FILE);
+ return getInstance(sensorId, LEGACY_FINGERPRINT_FILE);
}
private FingerprintUtils(String fileName) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index fd1181b616c7..3e13c45d335e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
@@ -59,6 +60,15 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
}
@Override
+ public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
+ super.onEnrollResult(identifier, remaining);
+
+ if (remaining == 0) {
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ }
+ }
+
+ @Override
protected boolean hasReachedEnrollmentLimit() {
return FingerprintUtils.getInstance(getSensorId())
.getBiometricsForUser(getContext(), getTargetUserId()).size()
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 65ce34d31b61..74549b917e82 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -144,7 +144,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
Utils.checkPermission(mContext, TEST_BIOMETRIC);
// Fake authentication with any of the existing fingers
- List<Fingerprint> fingerprints = FingerprintUtils.getInstance()
+ List<Fingerprint> fingerprints = FingerprintUtils.getLegacyInstance(mSensorId)
.getBiometricsForUser(mContext, userId);
if (fingerprints.isEmpty()) {
Slog.w(TAG, "No fingerprints, returning");
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 7c5b7c92c1c6..b8d27aa61806 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -111,6 +111,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
private int mCurrentUserId = UserHandle.USER_NULL;
+ private final int mSensorId;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -167,13 +168,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
void onHardwareUnavailable();
}
+ private final int mSensorId;
@NonNull private final Context mContext;
@NonNull final Handler mHandler;
@NonNull final BiometricScheduler mScheduler;
@Nullable private Callback mCallback;
- HalResultController(@NonNull Context context, @NonNull Handler handler,
+ HalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
@NonNull BiometricScheduler scheduler) {
+ mSensorId = sensorId;
mContext = context;
mHandler = handler;
mScheduler = scheduler;
@@ -194,7 +197,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
final int currentUserId = client.getTargetUserId();
- final CharSequence name = FingerprintUtils.getInstance()
+ final CharSequence name = FingerprintUtils.getLegacyInstance(mSensorId)
.getUniqueName(mContext, currentUserId);
final Fingerprint fingerprint = new Fingerprint(name, groupId, fingerId, deviceId);
@@ -307,6 +310,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
+ mSensorId = sensorId;
mScheduler = scheduler;
mHandler = handler;
mActivityTaskManager = ActivityTaskManager.getInstance();
@@ -361,7 +365,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final Handler handler = new Handler(Looper.getMainLooper());
final BiometricScheduler scheduler =
new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
- final HalResultController controller = new HalResultController(context, handler, scheduler);
+ final HalResultController controller = new HalResultController(sensorId, context, handler,
+ scheduler);
return new Fingerprint21(context, scheduler, handler, sensorId, strength,
lockoutResetDispatcher, controller);
}
@@ -549,7 +554,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(),
+ hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController);
mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() {
@Override
@@ -624,7 +629,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
- userId, opPackageName, FingerprintUtils.getInstance(),
+ userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
mSensorProperties.sensorId, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
@@ -638,8 +643,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mSensorProperties.sensorId, userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
- mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(),
- mAuthenticatorIds);
+ mSensorProperties.sensorId, enrolledList,
+ FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client);
});
}
@@ -657,14 +662,15 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@Override
public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
mHandler.post(() -> {
- FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name);
+ FingerprintUtils.getLegacyInstance(mSensorId)
+ .renameBiometricForUser(mContext, userId, fingerId, name);
});
}
@Override
@NonNull
public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
- return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId);
+ return FingerprintUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId);
}
@Override
@@ -716,7 +722,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final long userToken = proto.start(SensorStateProto.USER_STATES);
proto.write(UserStateProto.USER_ID, userId);
- proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getInstance()
+ proto.write(UserStateProto.NUM_ENROLLED, FingerprintUtils.getLegacyInstance(mSensorId)
.getBiometricsForUser(mContext, userId).size());
proto.end(userToken);
}
@@ -737,7 +743,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
proto.write(FingerprintUserStatsProto.USER_ID, userId);
proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
- FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId).size());
+ FingerprintUtils.getLegacyInstance(mSensorId)
+ .getBiometricsForUser(mContext, userId).size());
// Normal fingerprint authentications (e.g. lockscreen)
long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
@@ -777,7 +784,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
JSONArray sets = new JSONArray();
for (UserInfo user : UserManager.get(mContext).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
- final int N = FingerprintUtils.getInstance()
+ final int N = FingerprintUtils.getLegacyInstance(mSensorId)
.getBiometricsForUser(mContext, userId).size();
JSONObject set = new JSONObject();
set.put("id", userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index e4933e40ccd5..791d224b9728 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -210,9 +210,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
@NonNull private Fingerprint21UdfpsMock mFingerprint21;
@Nullable private LastAuthArgs mLastAuthArgs;
- MockHalResultController(@NonNull Context context, @NonNull Handler handler,
+ MockHalResultController(int sensorId, @NonNull Context context, @NonNull Handler handler,
@NonNull BiometricScheduler scheduler) {
- super(context, handler, scheduler);
+ super(sensorId, context, handler, scheduler);
}
void init(@NonNull RestartAuthRunnable restartAuthRunnable,
@@ -280,7 +280,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
final TestableBiometricScheduler scheduler =
new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
final MockHalResultController controller =
- new MockHalResultController(context, handler, scheduler);
+ new MockHalResultController(sensorId, context, handler, scheduler);
return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId, strength,
lockoutResetDispatcher, controller);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 2a89a880b680..af61a8be3052 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
@@ -100,6 +101,15 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
}
@Override
+ public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
+ super.onEnrollResult(identifier, remaining);
+
+ if (remaining == 0) {
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ }
+ }
+
+ @Override
public void onPointerDown(int x, int y, float minor, float major) {
UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c7891865644a..1f0fb5e006b8 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -45,8 +45,8 @@ import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import java.net.InetAddress;
import java.util.Arrays;
@@ -279,7 +279,7 @@ public class DnsManager {
}
public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
- Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
+ Log.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
return (cfg != null)
? mPrivateDnsMap.put(network.netId, cfg)
: mPrivateDnsMap.remove(network.netId);
@@ -389,7 +389,7 @@ public class DnsManager {
mPrivateDnsValidationMap.remove(netId);
}
- Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+ Log.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+ "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
paramsParcel.successThreshold, paramsParcel.minSamples,
@@ -400,7 +400,7 @@ public class DnsManager {
try {
mDnsResolver.setResolverConfiguration(paramsParcel);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error setting DNS configuration: " + e);
+ Log.e(TAG, "Error setting DNS configuration: " + e);
return;
}
}
@@ -431,8 +431,8 @@ public class DnsManager {
DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
if (mSampleValidity < 0 || mSampleValidity > 65535) {
- Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
- DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+ Log.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default="
+ + DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
}
@@ -440,17 +440,17 @@ public class DnsManager {
DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
- Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
- DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+ Log.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default="
+ + DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
}
mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
- Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
- "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
- DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
+ Log.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples
+ + "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", "
+ + DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
}
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 17828a0549b3..21ef356c962c 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -60,8 +60,8 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.DebugUtils;
+import android.util.Log;
import android.util.Range;
-import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -230,7 +230,7 @@ public class MultipathPolicyTracker {
mUsageCallback = new UsageCallback() {
@Override
public void onThresholdReached(int networkType, String subscriberId) {
- if (DBG) Slog.d(TAG, "onThresholdReached for network " + network);
+ if (DBG) Log.d(TAG, "onThresholdReached for network " + network);
mMultipathBudget = 0;
updateMultipathBudget();
}
@@ -252,7 +252,7 @@ public class MultipathPolicyTracker {
final long bytes = getNetworkTotalBytes(
start.toInstant().toEpochMilli(),
end.toInstant().toEpochMilli());
- if (DBG) Slog.d(TAG, "Non-default data usage: " + bytes);
+ if (DBG) Log.d(TAG, "Non-default data usage: " + bytes);
return bytes;
}
@@ -261,7 +261,7 @@ public class MultipathPolicyTracker {
return LocalServices.getService(NetworkStatsManagerInternal.class)
.getNetworkTotalBytes(mNetworkTemplate, start, end);
} catch (RuntimeException e) {
- Slog.w(TAG, "Failed to get data usage: " + e);
+ Log.w(TAG, "Failed to get data usage: " + e);
return -1;
}
}
@@ -326,17 +326,17 @@ public class MultipathPolicyTracker {
void updateMultipathBudget() {
long quota = LocalServices.getService(NetworkPolicyManagerInternal.class)
.getSubscriptionOpportunisticQuota(this.network, QUOTA_TYPE_MULTIPATH);
- if (DBG) Slog.d(TAG, "Opportunistic quota from data plan: " + quota + " bytes");
+ if (DBG) Log.d(TAG, "Opportunistic quota from data plan: " + quota + " bytes");
// Fallback to user settings-based quota if not available from phone plan
if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) {
quota = getUserPolicyOpportunisticQuotaBytes();
- if (DBG) Slog.d(TAG, "Opportunistic quota from user policy: " + quota + " bytes");
+ if (DBG) Log.d(TAG, "Opportunistic quota from user policy: " + quota + " bytes");
}
if (quota == OPPORTUNISTIC_QUOTA_UNKNOWN) {
quota = getDefaultDailyMultipathQuotaBytes();
- if (DBG) Slog.d(TAG, "Setting quota: " + quota + " bytes");
+ if (DBG) Log.d(TAG, "Setting quota: " + quota + " bytes");
}
// TODO: re-register if day changed: budget may have run out but should be refreshed.
@@ -344,7 +344,7 @@ public class MultipathPolicyTracker {
// If there is already a usage callback pending , there's no need to re-register it
// if the quota hasn't changed. The callback will simply fire as expected when the
// budget is spent.
- if (DBG) Slog.d(TAG, "Quota still " + quota + ", not updating.");
+ if (DBG) Log.d(TAG, "Quota still " + quota + ", not updating.");
return;
}
mQuota = quota;
@@ -364,8 +364,9 @@ public class MultipathPolicyTracker {
// since last time, so even if this is called very often the budget will not snap to 0
// as soon as there are less than 2MB left for today.
if (budget > NetworkStatsManager.MIN_THRESHOLD_BYTES) {
- if (DBG) Slog.d(TAG, "Setting callback for " + budget +
- " bytes on network " + network);
+ if (DBG) {
+ Log.d(TAG, "Setting callback for " + budget + " bytes on network " + network);
+ }
registerUsageCallback(budget);
} else {
maybeUnregisterUsageCallback();
@@ -402,7 +403,7 @@ public class MultipathPolicyTracker {
private void maybeUnregisterUsageCallback() {
if (haveMultipathBudget()) {
- if (DBG) Slog.d(TAG, "Unregistering callback, budget was " + mMultipathBudget);
+ if (DBG) Log.d(TAG, "Unregistering callback, budget was " + mMultipathBudget);
mStatsManager.unregisterUsageCallback(mUsageCallback);
mMultipathBudget = 0;
}
@@ -467,9 +468,9 @@ public class MultipathPolicyTracker {
try {
mMultipathTrackers.put(network, new MultipathTracker(network, nc));
} catch (IllegalStateException e) {
- Slog.e(TAG, "Can't track mobile network " + network + ": " + e.getMessage());
+ Log.e(TAG, "Can't track mobile network " + network + ": " + e.getMessage());
}
- if (DBG) Slog.d(TAG, "Tracking mobile network " + network);
+ if (DBG) Log.d(TAG, "Tracking mobile network " + network);
}
@Override
@@ -479,7 +480,7 @@ public class MultipathPolicyTracker {
existing.shutdown();
mMultipathTrackers.remove(network);
}
- if (DBG) Slog.d(TAG, "No longer tracking mobile network " + network);
+ if (DBG) Log.d(TAG, "No longer tracking mobile network " + network);
}
};
@@ -524,16 +525,16 @@ public class MultipathPolicyTracker {
@Override
public void onChange(boolean selfChange) {
- Slog.wtf(TAG, "Should never be reached.");
+ Log.wtf(TAG, "Should never be reached.");
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (!Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES)
.equals(uri)) {
- Slog.wtf(TAG, "Unexpected settings observation: " + uri);
+ Log.wtf(TAG, "Unexpected settings observation: " + uri);
}
- if (DBG) Slog.d(TAG, "Settings change: updating budgets.");
+ if (DBG) Log.d(TAG, "Settings change: updating budgets.");
updateAllMultipathBudgets();
}
}
@@ -541,7 +542,7 @@ public class MultipathPolicyTracker {
private final class ConfigChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (DBG) Slog.d(TAG, "Configuration change: updating budgets.");
+ if (DBG) Log.d(TAG, "Configuration change: updating budgets.");
updateAllMultipathBudgets();
}
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 163788f4a4df..d9c2e80aadcd 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -30,7 +30,7 @@ import android.net.RouteInfo;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -176,7 +176,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
try {
mNMService.registerObserver(this);
} catch (RemoteException e) {
- Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
+ Log.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
return;
}
@@ -185,7 +185,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
try {
addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+ Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
}
mIface = CLAT_PREFIX + baseIface;
mBaseIface = baseIface;
@@ -193,7 +193,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
try {
mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr);
} catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
- Slog.e(TAG, "Invalid IPv6 address " + addrStr);
+ Log.e(TAG, "Invalid IPv6 address " + addrStr);
}
if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
stopPrefixDiscovery();
@@ -218,7 +218,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
try {
mNMService.unregisterObserver(this);
} catch (RemoteException | IllegalStateException e) {
- Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
+ Log.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
}
mNat64PrefixInUse = null;
mIface = null;
@@ -242,37 +242,37 @@ public class Nat464Xlat extends BaseNetworkObserver {
@VisibleForTesting
protected void start() {
if (isStarted()) {
- Slog.e(TAG, "startClat: already started");
+ Log.e(TAG, "startClat: already started");
return;
}
if (mNetwork.linkProperties == null) {
- Slog.e(TAG, "startClat: Can't start clat with null LinkProperties");
+ Log.e(TAG, "startClat: Can't start clat with null LinkProperties");
return;
}
String baseIface = mNetwork.linkProperties.getInterfaceName();
if (baseIface == null) {
- Slog.e(TAG, "startClat: Can't start clat on null interface");
+ Log.e(TAG, "startClat: Can't start clat on null interface");
return;
}
// TODO: should we only do this if mNetd.clatdStart() succeeds?
- Slog.i(TAG, "Starting clatd on " + baseIface);
+ Log.i(TAG, "Starting clatd on " + baseIface);
enterStartingState(baseIface);
}
@VisibleForTesting
protected void stop() {
if (!isStarted()) {
- Slog.e(TAG, "stopClat: already stopped");
+ Log.e(TAG, "stopClat: already stopped");
return;
}
- Slog.i(TAG, "Stopping clatd on " + mBaseIface);
+ Log.i(TAG, "Stopping clatd on " + mBaseIface);
try {
mNetd.clatdStop(mBaseIface);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+ Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
}
String iface = mIface;
@@ -294,7 +294,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
try {
mDnsResolver.startPrefix64Discovery(getNetId());
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
+ Log.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
}
mPrefixDiscoveryRunning = true;
}
@@ -303,7 +303,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
try {
mDnsResolver.stopPrefix64Discovery(getNetId());
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
+ Log.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
}
mPrefixDiscoveryRunning = false;
}
@@ -320,7 +320,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
try {
mDnsResolver.setPrefix64(getNetId(), prefixString);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+ Log.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+ prefix + ": " + e);
}
}
@@ -328,7 +328,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
private void maybeHandleNat64PrefixChange() {
final IpPrefix newPrefix = selectNat64Prefix();
if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
- Slog.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
+ Log.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
+ newPrefix);
stop();
// It's safe to call update here, even though this method is called from update, because
@@ -418,7 +418,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
return;
}
- Slog.d(TAG, "clatd running, updating NAI for " + mIface);
+ Log.d(TAG, "clatd running, updating NAI for " + mIface);
for (LinkProperties stacked: oldLp.getStackedLinks()) {
if (Objects.equals(mIface, stacked.getInterfaceName())) {
lp.addStackedLink(stacked);
@@ -451,7 +451,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
return new LinkAddress(
InetAddresses.parseNumericAddress(config.ipv4Addr), config.prefixLength);
} catch (IllegalArgumentException | RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error getting link properties: " + e);
+ Log.e(TAG, "Error getting link properties: " + e);
return null;
}
}
@@ -480,11 +480,11 @@ public class Nat464Xlat extends BaseNetworkObserver {
LinkAddress clatAddress = getLinkAddress(iface);
if (clatAddress == null) {
- Slog.e(TAG, "clatAddress was null for stacked iface " + iface);
+ Log.e(TAG, "clatAddress was null for stacked iface " + iface);
return;
}
- Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
+ Log.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
mIface, mIface, mBaseIface));
enterRunningState();
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
@@ -503,7 +503,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
return;
}
- Slog.i(TAG, "interface " + iface + " removed");
+ Log.i(TAG, "interface " + iface + " removed");
// If we're running, and the interface was removed, then we didn't call stop(), and it's
// likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling
// stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 7f4fb4039d4e..7795ed38a74f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -35,7 +35,7 @@ import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.Slog;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;
@@ -175,7 +175,7 @@ public class NetworkNotificationManager {
final int previousEventId = mNotificationTypeMap.get(id);
final NotificationType previousNotifyType = NotificationType.getFromId(previousEventId);
if (priority(previousNotifyType) > priority(notifyType)) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"ignoring notification %s for network %s with existing notification %s",
notifyType, id, previousNotifyType));
return;
@@ -183,7 +183,7 @@ public class NetworkNotificationManager {
clearNotification(id);
if (DBG) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"showNotification tag=%s event=%s transport=%s name=%s highPriority=%s",
tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
}
@@ -253,7 +253,7 @@ public class NetworkNotificationManager {
// are sent, but they are not implemented yet.
return;
} else {
- Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
+ Log.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
+ getTransportName(transportType));
return;
}
@@ -294,7 +294,7 @@ public class NetworkNotificationManager {
try {
mNotificationManager.notify(tag, eventId, notification);
} catch (NullPointerException npe) {
- Slog.d(TAG, "setNotificationVisible: visible notificationManager error", npe);
+ Log.d(TAG, "setNotificationVisible: visible notificationManager error", npe);
}
}
@@ -317,13 +317,13 @@ public class NetworkNotificationManager {
final String tag = tagFor(id);
final int eventId = mNotificationTypeMap.get(id);
if (DBG) {
- Slog.d(TAG, String.format("clearing notification tag=%s event=%s", tag,
+ Log.d(TAG, String.format("clearing notification tag=%s event=%s", tag,
nameOf(eventId)));
}
try {
mNotificationManager.cancel(tag, eventId);
} catch (NullPointerException npe) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"failed to clear notification tag=%s event=%s", tag, nameOf(eventId)), npe);
}
mNotificationTypeMap.delete(id);
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index 26cc3ee165f1..5cb3d94a929f 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -35,7 +35,7 @@ import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -168,7 +168,7 @@ public class ProxyTracker {
proxyProperties = new ProxyInfo(host, port, exclList);
}
if (!proxyProperties.isValid()) {
- if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+ if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
return;
}
@@ -223,7 +223,7 @@ public class ProxyTracker {
if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) {
return;
}
- if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
+ if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -255,7 +255,7 @@ public class ProxyTracker {
if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) ||
!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) {
if (!proxyInfo.isValid()) {
- if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+ if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
return;
}
mGlobalProxy = new ProxyInfo(proxyInfo);
@@ -296,7 +296,7 @@ public class ProxyTracker {
synchronized (mProxyLock) {
if (Objects.equals(mDefaultProxy, proxyInfo)) return;
if (proxyInfo != null && !proxyInfo.isValid()) {
- if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+ if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
return;
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index a2c427b8036a..027b9afba392 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -76,8 +76,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -296,8 +295,8 @@ public final class ContentService extends IContentService.Stub {
// Let the package manager query for the sync adapters for a given authority
// as we grant default permissions to sync adapters for specific authorities.
- final PermissionManagerServiceInternal permissionManagerInternal =
- LocalServices.getService(PermissionManagerServiceInternal.class);
+ final LegacyPermissionManagerInternal permissionManagerInternal =
+ LocalServices.getService(LegacyPermissionManagerInternal.class);
permissionManagerInternal.setSyncAdapterPackagesProvider((authority, userId) -> {
return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
});
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index d27cb16ecc51..668142b7e8ee 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1804,10 +1804,8 @@ public class SyncStorageEngine {
int id = -1;
try {
id = parser.getAttributeInt(null, "id");
- } catch (NumberFormatException e) {
+ } catch (XmlPullParserException e) {
Slog.e(TAG, "error parsing the id of the authority", e);
- } catch (NullPointerException e) {
- Slog.e(TAG, "the id of the authority is null", e);
}
if (id >= 0) {
String authorityName = parser.getAttributeValue(null, "authority");
@@ -1920,28 +1918,26 @@ public class SyncStorageEngine {
private void parseExtra(TypedXmlPullParser parser, Bundle extras) {
String name = parser.getAttributeValue(null, "name");
String type = parser.getAttributeValue(null, "type");
- String value1 = parser.getAttributeValue(null, "value1");
- String value2 = parser.getAttributeValue(null, "value2");
try {
if ("long".equals(type)) {
- extras.putLong(name, Long.parseLong(value1));
+ extras.putLong(name, parser.getAttributeLong(null, "value1"));
} else if ("integer".equals(type)) {
- extras.putInt(name, Integer.parseInt(value1));
+ extras.putInt(name, parser.getAttributeInt(null, "value1"));
} else if ("double".equals(type)) {
- extras.putDouble(name, Double.parseDouble(value1));
+ extras.putDouble(name, parser.getAttributeDouble(null, "value1"));
} else if ("float".equals(type)) {
- extras.putFloat(name, Float.parseFloat(value1));
+ extras.putFloat(name, parser.getAttributeFloat(null, "value1"));
} else if ("boolean".equals(type)) {
- extras.putBoolean(name, Boolean.parseBoolean(value1));
+ extras.putBoolean(name, parser.getAttributeBoolean(null, "value1"));
} else if ("string".equals(type)) {
- extras.putString(name, value1);
+ extras.putString(name, parser.getAttributeValue(null, "value1"));
} else if ("account".equals(type)) {
+ final String value1 = parser.getAttributeValue(null, "value1");
+ final String value2 = parser.getAttributeValue(null, "value2");
extras.putParcelable(name, new Account(value1, value2));
}
- } catch (NumberFormatException e) {
- Slog.e(TAG, "error parsing bundle value", e);
- } catch (NullPointerException e) {
+ } catch (XmlPullParserException e) {
Slog.e(TAG, "error parsing bundle value", e);
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2a0e21919704..7a8ba9f4380c 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -668,12 +668,11 @@ public class BrightnessTracker {
builder.setUserBrightnessPoint(
parser.getAttributeBoolean(null, ATTR_USER_POINT, false));
- String colorSampleDurationString =
- parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION);
+ long colorSampleDuration =
+ parser.getAttributeLong(null, ATTR_COLOR_SAMPLE_DURATION, -1);
String colorValueBucketsString =
parser.getAttributeValue(null, ATTR_COLOR_VALUE_BUCKETS);
- if (colorSampleDurationString != null && colorValueBucketsString != null) {
- long colorSampleDuration = Long.parseLong(colorSampleDurationString);
+ if (colorSampleDuration != -1 && colorValueBucketsString != null) {
String[] buckets = colorValueBucketsString.split(",");
long[] bucketValues = new long[buckets.length];
for (int i = 0; i < bucketValues.length; ++i) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 23e5a43a359f..a8e56a1187cf 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -73,15 +73,31 @@ public class DisplayDeviceConfig {
public static DisplayDeviceConfig create(long physicalDisplayId) {
DisplayDeviceConfig config;
+ config = loadConfigFromDirectory(Environment.getProductDirectory(), physicalDisplayId);
+ if (config != null) {
+ return config;
+ }
+
+ config = loadConfigFromDirectory(Environment.getVendorDirectory(), physicalDisplayId);
+ if (config != null) {
+ return config;
+ }
+
+ return null;
+ }
+
+ private static DisplayDeviceConfig loadConfigFromDirectory(
+ File baseDirectory, long physicalDisplayId) {
+ DisplayDeviceConfig config;
// Create config using filename from physical ID (including "stable" bit).
- config = getConfigFromSuffix(STABLE_ID_SUFFIX_FORMAT, physicalDisplayId);
+ config = getConfigFromSuffix(baseDirectory, STABLE_ID_SUFFIX_FORMAT, physicalDisplayId);
if (config != null) {
return config;
}
// Create config using filename from physical ID (excluding "stable" bit).
final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
- config = getConfigFromSuffix(NO_SUFFIX_FORMAT, withoutStableFlag);
+ config = getConfigFromSuffix(baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
if (config != null) {
return config;
}
@@ -90,7 +106,7 @@ public class DisplayDeviceConfig {
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
int port = physicalAddress.getPort();
- config = getConfigFromSuffix(PORT_SUFFIX_FORMAT, port);
+ config = getConfigFromSuffix(baseDirectory, PORT_SUFFIX_FORMAT, port);
if (config != null) {
return config;
}
@@ -127,12 +143,13 @@ public class DisplayDeviceConfig {
return str;
}
- private static DisplayDeviceConfig getConfigFromSuffix(String suffixFormat, long idNumber) {
+ private static DisplayDeviceConfig getConfigFromSuffix(File baseDirectory,
+ String suffixFormat, long idNumber) {
final String suffix = String.format(suffixFormat, idNumber);
final String filename = String.format(CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
- Environment.getProductDirectory(), ETC_DIR, DISPLAY_CONFIG_DIR, filename);
+ baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
if (filePath.exists()) {
final DisplayDeviceConfig config = new DisplayDeviceConfig();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 29b413d921e1..d4a19d6bc366 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -53,6 +53,7 @@ import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
@@ -1252,11 +1253,19 @@ public final class DisplayManagerService extends SystemService {
mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
}
-
boolean shouldAlwaysRespectAppRequestedModeInternal() {
return mDisplayModeDirector.shouldAlwaysRespectAppRequestedMode();
}
+ void setRefreshRateSwitchingTypeInternal(@DisplayManager.SwitchingType int newValue) {
+ mDisplayModeDirector.setModeSwitchingType(newValue);
+ }
+
+ @DisplayManager.SwitchingType
+ int getRefreshRateSwitchingTypeInternal() {
+ return mDisplayModeDirector.getModeSwitchingType();
+ }
+
private void setBrightnessConfigurationForUserInternal(
@Nullable BrightnessConfiguration c, @UserIdInt int userId,
@Nullable String packageName) {
@@ -2595,6 +2604,32 @@ public final class DisplayManagerService extends SystemService {
}
}
+ @Override // Binder call
+ public void setRefreshRateSwitchingType(int newValue) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
+ "Permission required to modify refresh rate switching type.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setRefreshRateSwitchingTypeInternal(newValue);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public int getRefreshRateSwitchingType() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
+ "Permission required read refresh rate switching type.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getRefreshRateSwitchingTypeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 329081a8391f..95918945eecd 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -16,7 +16,6 @@
package com.android.server.display;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -53,8 +52,6 @@ import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -110,28 +107,11 @@ public class DisplayModeDirector {
private boolean mAlwaysRespectAppRequest;
- @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
- SWITCHING_TYPE_NONE,
- SWITCHING_TYPE_WITHIN_GROUPS,
- SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SwitchingType {}
-
- // No mode switching will happen.
- public static final int SWITCHING_TYPE_NONE = 0;
- // Allow only refresh rate switching between modes in the same configuration group. This way
- // only switches without visual interruptions for the user will be allowed.
- public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1;
- // Allow refresh rate switching between all refresh rates even if the switch with have visual
- // interruptions for the user.
- public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2;
-
/**
* The allowed refresh rate switching type. This is used by SurfaceFlinger.
*/
- @SwitchingType
- private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS;
+ @DisplayManager.SwitchingType
+ private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
this(context, handler, new RealInjector());
@@ -337,7 +317,7 @@ public class DisplayModeDirector {
if (availableModes.length > 0) {
baseModeId = availableModes[0];
}
- if (mModeSwitchingType == SWITCHING_TYPE_NONE) {
+ if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
Display.Mode baseMode = null;
for (Display.Mode mode : modes) {
if (mode.getModeId() == baseModeId) {
@@ -359,7 +339,7 @@ public class DisplayModeDirector {
}
boolean allowGroupSwitching =
- mModeSwitchingType == SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
+ mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
return new DesiredDisplayModeSpecs(baseModeId,
allowGroupSwitching,
new RefreshRateRange(
@@ -450,18 +430,21 @@ public class DisplayModeDirector {
/**
* Sets the display mode switching type.
- * @param type
+ * @param newType
*/
- public void setModeSwitchingType(@SwitchingType int type) {
+ public void setModeSwitchingType(@DisplayManager.SwitchingType int newType) {
synchronized (mLock) {
- mModeSwitchingType = type;
+ if (newType != mModeSwitchingType) {
+ mModeSwitchingType = newType;
+ notifyDesiredDisplayModeSpecsChangedLocked();
+ }
}
}
/**
* Returns the display mode switching type.
*/
- @SwitchingType
+ @DisplayManager.SwitchingType
public int getModeSwitchingType() {
synchronized (mLock) {
return mModeSwitchingType;
@@ -583,13 +566,13 @@ public class DisplayModeDirector {
}
}
- private static String switchingTypeToString(@SwitchingType int type) {
+ private static String switchingTypeToString(@DisplayManager.SwitchingType int type) {
switch (type) {
- case SWITCHING_TYPE_NONE:
+ case DisplayManager.SWITCHING_TYPE_NONE:
return "SWITCHING_TYPE_NONE";
- case SWITCHING_TYPE_WITHIN_GROUPS:
+ case DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS:
return "SWITCHING_TYPE_WITHIN_GROUPS";
- case SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
+ case DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS:
return "SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS";
default:
return "Unknown SwitchingType " + type;
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index b0820e81ec09..a62642b1f842 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -31,17 +31,12 @@ import android.util.Xml;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -49,7 +44,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -628,15 +622,7 @@ final class PersistentDataStore {
}
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
- String timeStampString = parser.getAttributeValue(null, ATTR_TIME_STAMP);
- long timeStamp = -1;
- if (timeStampString != null) {
- try {
- timeStamp = Long.parseLong(timeStampString);
- } catch (NumberFormatException nfe) {
- // Ignore we will just not restore the timestamp.
- }
- }
+ long timeStamp = parser.getAttributeLong(null, ATTR_TIME_STAMP, -1);
try {
BrightnessConfiguration config =
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 22921ad1ecc2..521ce6973522 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -64,6 +64,9 @@ public final class FontManagerService {
@Nullable
private SharedMemory getSerializedSystemFontMap() {
+ if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+ return null;
+ }
synchronized (FontManagerService.this) {
if (mSerializedSystemFontMap == null) {
mSerializedSystemFontMap = createSerializedSystemFontMapLocked();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
index 2878a94fb860..a9eb75da77cb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
@@ -18,9 +18,14 @@ package com.android.server.hdmi;
import android.stats.hdmi.HdmiStatsEnums;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-class HdmiCecAtomWriter {
+/**
+ * Provides methods for writing HDMI-CEC statsd atoms.
+ */
+@VisibleForTesting
+public class HdmiCecAtomWriter {
private static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100;
private static final int ERROR_CODE_UNKNOWN = -1;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index a261fa1f2741..98d130f1ef69 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -253,7 +253,7 @@ public class HdmiCecConfig {
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return STORAGE_GLOBAL_SETTINGS;
- case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+ case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return STORAGE_SHARED_PREFS;
@@ -271,7 +271,7 @@ public class HdmiCecConfig {
return Global.HDMI_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return Global.HDMI_CEC_VERSION;
- case HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP:
+ case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return setting.getName();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 19dc017a35d5..bbd5ac3b5ccc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -635,7 +635,7 @@ final class HdmiCecController {
*/
private int incomingMessageDirection(int srcAddress, int dstAddress) {
boolean sourceIsLocal = false;
- boolean destinationIsLocal = false;
+ boolean destinationIsLocal = dstAddress == Constants.ADDR_BROADCAST;
for (HdmiCecLocalDevice localDevice : mService.getHdmiCecNetwork().getLocalDeviceList()) {
int logicalAddress = localDevice.getDeviceInfo().getLogicalAddress();
if (logicalAddress == srcAddress) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 9ca1f913f5ce..e6cf18b8db3d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -160,20 +160,20 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
switch (standbyAction) {
case HdmiControlService.STANDBY_SCREEN_OFF:
// Get latest setting value
- @HdmiControlManager.StandbyBehavior
+ @HdmiControlManager.PowerControlMode
String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
switch (sendStandbyOnSleep) {
- case HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV:
+ case HdmiControlManager.POWER_CONTROL_MODE_TV:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
break;
- case HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST:
+ case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress,
Constants.ADDR_BROADCAST));
break;
- case HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE:
+ case HdmiControlManager.POWER_CONTROL_MODE_NONE:
break;
}
break;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 59ebdd597279..8a1a728ba8dd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -107,8 +107,8 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
protected void sendStandby(int deviceId) {
assertRunOnServiceThread();
String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
- if (sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST)) {
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+ if (sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
return;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 5d75a63d0c8d..fc21724714c5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -59,7 +59,8 @@ import java.util.concurrent.ArrayBlockingQueue;
* Note that the information cached in this class is not guaranteed to be up-to-date, especially OSD
* names, power states can be outdated.
*/
-class HdmiCecNetwork {
+@VisibleForTesting
+public class HdmiCecNetwork {
private static final String TAG = "HdmiCecNetwork";
protected final Object mLock;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 56b73ba04d89..a1d13e974019 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -564,7 +564,8 @@ public class HdmiControlService extends SystemService {
mTvInputManager.unregisterCallback(callback);
}
- PowerManager getPowerManager() {
+ @VisibleForTesting
+ protected PowerManager getPowerManager() {
return mPowerManager;
}
@@ -729,7 +730,8 @@ public class HdmiControlService extends SystemService {
Global.putInt(cr, key, toInt(value));
}
- void writeStringSystemProperty(String key, String value) {
+ @VisibleForTesting
+ protected void writeStringSystemProperty(String key, String value) {
SystemProperties.set(key, value);
}
@@ -3366,7 +3368,7 @@ public class HdmiControlService extends SystemService {
}
@VisibleForTesting
- HdmiCecAtomWriter getAtomWriter() {
+ protected HdmiCecAtomWriter getAtomWriter() {
return mAtomWriter;
}
@@ -3395,7 +3397,8 @@ public class HdmiControlService extends SystemService {
HdmiControlService.PERMISSION);
}
- HdmiCecConfig getHdmiCecConfig() {
+ @VisibleForTesting
+ protected HdmiCecConfig getHdmiCecConfig() {
return mHdmiCecConfig;
}
}
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index f9f97fe7b184..ea6e61582ea0 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -165,9 +165,9 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
if (service.isAudioSystemDevice()) {
return false;
}
- @HdmiControlManager.StandbyBehavior String sendStandbyOnSleep =
+ @HdmiControlManager.PowerControlMode String sendStandbyOnSleep =
service.getHdmiCecConfig().getStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
- return sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+ return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
}
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index a735a8f9d305..6cec2723b141 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -510,7 +510,7 @@ final class PersistentDataStore {
serializer.startTag(null, "keyboard-layout");
serializer.attribute(null, "descriptor", layout);
if (layout.equals(mCurrentKeyboardLayout)) {
- serializer.attribute(null, "current", "true");
+ serializer.attributeBoolean(null, "current", true);
}
serializer.endTag(null, "keyboard-layout");
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index e5c198646fb6..c3a10c7a4f8a 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -113,7 +113,7 @@ import com.android.server.location.provider.MockLocationProvider;
import com.android.server.location.provider.PassiveLocationProvider;
import com.android.server.location.provider.PassiveLocationProviderManager;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -261,8 +261,8 @@ public class LocationManagerService extends ILocationManager.Stub {
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
- PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
- PermissionManagerServiceInternal.class);
+ LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
+ LegacyPermissionManagerInternal.class);
permissionManagerInternal.setLocationPackagesProvider(
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames));
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
index b4bcd7b10c42..fd8cf707e697 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -55,7 +55,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
@Override
void onInitialize() {
- mProxy.setListener(new LocationTimeZoneProviderProxy.Listener() {
+ mProxy.initialize(new LocationTimeZoneProviderProxy.Listener() {
@Override
public void onReportLocationTimeZoneEvent(
@NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
index b7c7476844a5..c8a1db6681ff 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.location.timezone;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -27,6 +28,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
@@ -179,36 +181,45 @@ public class LocationTimeZoneManagerService extends Binder {
}
private LocationTimeZoneProvider createPrimaryProvider() {
+ Resources resources = mContext.getResources();
+ if (!resources.getBoolean(R.bool.config_enablePrimaryLocationTimeZoneProvider)) {
+ return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME);
+ }
+
LocationTimeZoneProviderProxy proxy;
if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) {
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
} else {
- proxy = RealLocationTimeZoneProviderProxy.createAndRegister(
+ proxy = new RealLocationTimeZoneProviderProxy(
mContext,
mThreadingDomain,
PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
- com.android.internal.R.bool.config_enablePrimaryLocationTimeZoneOverlay,
- com.android.internal.R.string.config_primaryLocationTimeZoneProviderPackageName
+ R.bool.config_enablePrimaryLocationTimeZoneOverlay,
+ R.string.config_primaryLocationTimeZoneProviderPackageName
);
}
- return createLocationTimeZoneProvider(PRIMARY_PROVIDER_NAME, proxy);
+ return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
}
private LocationTimeZoneProvider createSecondaryProvider() {
+ Resources resources = mContext.getResources();
+ if (!resources.getBoolean(R.bool.config_enableSecondaryLocationTimeZoneProvider)) {
+ return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME);
+ }
+
LocationTimeZoneProviderProxy proxy;
if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) {
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
} else {
- proxy = RealLocationTimeZoneProviderProxy.createAndRegister(
+ proxy = new RealLocationTimeZoneProviderProxy(
mContext,
mThreadingDomain,
SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
- com.android.internal.R.bool.config_enableSecondaryLocationTimeZoneOverlay,
- com.android.internal.R.string
- .config_secondaryLocationTimeZoneProviderPackageName
+ R.bool.config_enableSecondaryLocationTimeZoneOverlay,
+ R.string.config_secondaryLocationTimeZoneProviderPackageName
);
}
- return createLocationTimeZoneProvider(SECONDARY_PROVIDER_NAME, proxy);
+ return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
}
private boolean isInSimulationMode(String providerName) {
@@ -216,21 +227,6 @@ public class LocationTimeZoneManagerService extends Binder {
SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false);
}
- private LocationTimeZoneProvider createLocationTimeZoneProvider(
- @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) {
- LocationTimeZoneProvider provider;
- if (proxy != null) {
- debugLog("LocationTimeZoneProvider found for providerName=" + providerName);
- provider = new BinderLocationTimeZoneProvider(mThreadingDomain,
- providerName, proxy);
- } else {
- debugLog("No LocationTimeZoneProvider found for providerName=" + providerName
- + ": stubbing");
- provider = new NullLocationTimeZoneProvider(mThreadingDomain, providerName);
- }
- return provider;
- }
-
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
index 1b4706f8801e..8a0259d12a6c 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
@@ -70,9 +70,11 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable {
}
/**
- * Sets the listener. The listener can expect to receive all events after this point.
+ * Initializes the proxy. The supplied listener can expect to receive all events after this
+ * point. This method also calls {@link #onInitialize()} for subclasses to handle their own
+ * initialization.
*/
- void setListener(@NonNull Listener listener) {
+ void initialize(@NonNull Listener listener) {
Objects.requireNonNull(listener);
synchronized (mSharedLock) {
if (mListener != null) {
@@ -80,9 +82,15 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable {
}
this.mListener = listener;
}
+ onInitialize();
}
/**
+ * Initializes the proxy. This is called after {@link #mListener} is set.
+ */
+ abstract void onInitialize();
+
+ /**
* Sets a new request for the provider.
*/
abstract void setRequest(@NonNull LocationTimeZoneProviderRequest request);
diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
index cd6d3592af0e..1a012882305f 100644
--- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
@@ -41,23 +41,6 @@ import java.util.Objects;
*/
class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
- /**
- * Creates and registers this proxy. If no suitable service is available for the proxy, returns
- * null.
- */
- @Nullable
- static LocationTimeZoneProviderProxy createAndRegister(
- @NonNull Context context, @NonNull ThreadingDomain threadingDomain,
- @NonNull String action, int enableOverlayResId, int nonOverlayPackageResId) {
- RealLocationTimeZoneProviderProxy proxy = new RealLocationTimeZoneProviderProxy(
- context, threadingDomain, action, enableOverlayResId, nonOverlayPackageResId);
- if (proxy.register()) {
- return proxy;
- } else {
- return null;
- }
- }
-
@NonNull private final ServiceWatcher mServiceWatcher;
@GuardedBy("mProxyLock")
@@ -66,7 +49,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
@GuardedBy("mProxyLock")
@NonNull private LocationTimeZoneProviderRequest mRequest;
- private RealLocationTimeZoneProviderProxy(
+ RealLocationTimeZoneProviderProxy(
@NonNull Context context, @NonNull ThreadingDomain threadingDomain,
@NonNull String action, int enableOverlayResId,
int nonOverlayPackageResId) {
@@ -77,6 +60,13 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
enableOverlayResId, nonOverlayPackageResId);
}
+ @Override
+ void onInitialize() {
+ if (!register()) {
+ throw new IllegalStateException("Unable to register binder proxy");
+ }
+ }
+
private boolean register() {
boolean resolves = mServiceWatcher.checkServiceResolves();
if (resolves) {
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
index 604ff74e71ac..5e66a99a93fa 100644
--- a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
@@ -49,6 +49,11 @@ class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderPro
mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST;
}
+ @Override
+ void onInitialize() {
+ // No-op - nothing to do for the simulated provider.
+ }
+
void simulate(@NonNull SimulatedBinderProviderEvent event) {
mThreadingDomain.assertCurrentThread();
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 64c3c289163e..ea1d8da74185 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -37,7 +37,7 @@ import android.os.Handler;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.TextUtils;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -128,7 +128,7 @@ public class LockdownVpnTracker {
final int egressType = (egressInfo == null) ? TYPE_NONE : egressInfo.getType();
final String egressIface = (egressProp == null) ?
null : egressProp.getInterfaceName();
- Slog.d(TAG, "handleStateChanged: egress=" + egressType
+ Log.d(TAG, "handleStateChanged: egress=" + egressType
+ " " + mAcceptedEgressIface + "->" + egressIface);
if (egressDisconnected || egressChanged) {
@@ -149,7 +149,7 @@ public class LockdownVpnTracker {
} else if (egressInfo.isConnected() && !vpnInfo.isConnectedOrConnecting()) {
if (mProfile.isValidLockdownProfile()) {
- Slog.d(TAG, "Active network connected; starting VPN");
+ Log.d(TAG, "Active network connected; starting VPN");
EventLogTags.writeLockdownVpnConnecting(egressType);
showNotification(R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected);
@@ -160,11 +160,11 @@ public class LockdownVpnTracker {
mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
- Slog.e(TAG, "Failed to start VPN", e);
+ Log.e(TAG, "Failed to start VPN", e);
showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
}
} else {
- Slog.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
+ Log.e(TAG, "Invalid VPN profile; requires IP-based server and DNS");
showNotification(R.string.vpn_lockdown_error, R.drawable.vpn_disconnected);
}
@@ -172,8 +172,8 @@ public class LockdownVpnTracker {
final String iface = vpnConfig.interfaze;
final List<LinkAddress> sourceAddrs = vpnConfig.addresses;
- Slog.d(TAG, "VPN connected using iface=" + iface +
- ", sourceAddr=" + sourceAddrs.toString());
+ Log.d(TAG, "VPN connected using iface=" + iface
+ + ", sourceAddr=" + sourceAddrs.toString());
EventLogTags.writeLockdownVpnConnected(egressType);
showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
@@ -190,7 +190,7 @@ public class LockdownVpnTracker {
}
private void initLocked() {
- Slog.d(TAG, "initLocked()");
+ Log.d(TAG, "initLocked()");
mVpn.setEnableTeardown(false);
mVpn.setLockdown(true);
@@ -204,7 +204,7 @@ public class LockdownVpnTracker {
}
private void shutdownLocked() {
- Slog.d(TAG, "shutdownLocked()");
+ Log.d(TAG, "shutdownLocked()");
mAcceptedEgressIface = null;
mErrorCount = 0;
@@ -222,7 +222,7 @@ public class LockdownVpnTracker {
*/
@GuardedBy("mConnService.mVpns")
public void reset() {
- Slog.d(TAG, "reset()");
+ Log.d(TAG, "reset()");
synchronized (mStateLock) {
// cycle tracker, reset error count, and trigger retry
shutdownLocked();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 006d78e4a648..5bd352c8f8e8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -17,11 +17,13 @@ package com.android.server.net;
import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.os.Process.INVALID_UID;
@@ -339,6 +341,8 @@ public class NetworkPolicyLogger {
return FIREWALL_CHAIN_NAME_STANDBY;
case FIREWALL_CHAIN_POWERSAVE:
return FIREWALL_CHAIN_NAME_POWERSAVE;
+ case FIREWALL_CHAIN_RESTRICTED:
+ return FIREWALL_CHAIN_NAME_RESTRICTED;
default:
return String.valueOf(chain);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ee860e3ac6d7..eb7ef2bbeaf2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -277,6 +277,7 @@ import com.android.server.pm.PackageManagerService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.BackgroundActivityStartCallback;
import com.android.server.wm.WindowManagerInternal;
@@ -298,6 +299,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -371,6 +373,20 @@ public class NotificationManagerService extends SystemService {
RoleManager.ROLE_EMERGENCY
};
+ // Used for rate limiting toasts by package.
+ static final String TOAST_QUOTA_TAG = "toast_quota_tag";
+
+ // This constant defines rate limits applied to showing toasts. The numbers are set in a way
+ // such that an aggressive toast showing strategy would result in a roughly 1.5x longer wait
+ // time (before the package is allowed to show toasts again) each time the toast rate limit is
+ // reached. It's meant to protect the user against apps spamming them with toasts (either
+ // accidentally or on purpose).
+ private static final MultiRateLimiter.RateLimit[] TOAST_RATE_LIMITS = {
+ MultiRateLimiter.RateLimit.create(3, Duration.ofSeconds(20)),
+ MultiRateLimiter.RateLimit.create(5, Duration.ofSeconds(42)),
+ MultiRateLimiter.RateLimit.create(6, Duration.ofSeconds(68)),
+ };
+
// When #matchesCallFilter is called from the ringer, wait at most
// 3s to resolve the contacts. This timeout is required since
// ContactsProvider might take a long time to start up.
@@ -422,6 +438,16 @@ public class NotificationManagerService extends SystemService {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
+ /**
+ * Rate limit showing toasts, on a per package basis.
+ *
+ * It limits the effects of {@link android.widget.Toast#show()} calls to prevent overburdening
+ * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed
+ * in a certain time frame will result in the toast being discarded.
+ */
+ @ChangeId
+ private static final long RATE_LIMIT_TOASTS = 154198299L;
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -500,6 +526,9 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mToastQueue")
private boolean mIsCurrentToastShown = false;
+ // Used for rate limiting toasts by package.
+ private MultiRateLimiter mToastRateLimiter;
+
// The last key in this list owns the hardware.
ArrayList<String> mLights = new ArrayList<>();
@@ -1662,6 +1691,11 @@ public class NotificationManagerService extends SystemService {
= Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
private final Uri NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI
= Settings.Global.getUriFor(Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS);
+ private final Uri LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS
+ = Settings.Secure.getUriFor(
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ private final Uri LOCK_SCREEN_SHOW_NOTIFICATIONS
+ = Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
SettingsObserver(Handler handler) {
super(handler);
@@ -1681,6 +1715,11 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI,
false, this, UserHandle.USER_ALL);
+
+ resolver.registerContentObserver(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ false, this, UserHandle.USER_ALL);
update(null);
}
@@ -1722,6 +1761,12 @@ public class NotificationManagerService extends SystemService {
if (uri == null || NOTIFICATION_SHOW_MEDIA_ON_QUICK_SETTINGS_URI.equals(uri)) {
mPreferencesHelper.updateMediaNotificationFilteringEnabled();
}
+ if (uri == null || LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS.equals(uri)) {
+ mPreferencesHelper.updateLockScreenPrivateNotifications();
+ }
+ if (uri == null || LOCK_SCREEN_SHOW_NOTIFICATIONS.equals(uri)) {
+ mPreferencesHelper.updateLockScreenShowNotifications();
+ }
}
}
@@ -1915,7 +1960,8 @@ public class NotificationManagerService extends SystemService {
DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
NotificationHistoryManager historyManager, StatsManager statsManager,
- TelephonyManager telephonyManager, ActivityManagerInternal ami) {
+ TelephonyManager telephonyManager, ActivityManagerInternal ami,
+ MultiRateLimiter toastRateLimiter) {
mHandler = handler;
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2107,6 +2153,8 @@ public class NotificationManagerService extends SystemService {
com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos));
mStatsManager = statsManager;
+ mToastRateLimiter = toastRateLimiter;
+
// register for various Intents.
// If this is called within a test, make sure to unregister the intent receivers by
// calling onDestroy()
@@ -2217,7 +2265,8 @@ public class NotificationManagerService extends SystemService {
mStatsManager = (StatsManager) getContext().getSystemService(
Context.STATS_MANAGER),
getContext().getSystemService(TelephonyManager.class),
- LocalServices.getService(ActivityManagerInternal.class));
+ LocalServices.getService(ActivityManagerInternal.class),
+ createToastRateLimiter());
publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -2855,6 +2904,10 @@ public class NotificationManagerService extends SystemService {
return mInternalService;
}
+ private MultiRateLimiter createToastRateLimiter() {
+ return new MultiRateLimiter.Builder(getContext()).addRateLimits(TOAST_RATE_LIMITS).build();
+ }
+
@VisibleForTesting
final IBinder mService = new INotificationManager.Stub() {
// Toasts
@@ -3591,8 +3644,9 @@ public class NotificationManagerService extends SystemService {
public ParceledListSlice<ConversationChannelWrapper> getConversations(
boolean onlyImportant) {
enforceSystemOrSystemUI("getConversations");
+ IntArray userIds = mUserProfiles.getCurrentProfileIds();
ArrayList<ConversationChannelWrapper> conversations =
- mPreferencesHelper.getConversations(onlyImportant);
+ mPreferencesHelper.getConversations(userIds, onlyImportant);
for (ConversationChannelWrapper conversation : conversations) {
if (mShortcutHelper == null) {
conversation.setShortcutInfo(null);
@@ -7115,15 +7169,15 @@ public class NotificationManagerService extends SystemService {
if (record.getSbn().isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
return false;
}
- // not if in call or the screen's on
- if (isInCall() || mScreenOn) {
+ // not if in call
+ if (isInCall()) {
return false;
}
// check current user
if (!isNotificationForCurrentUser(record)) {
return false;
}
-
+ // Light, but only when the screen is off
return true;
}
@@ -7329,10 +7383,21 @@ public class NotificationManagerService extends SystemService {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
- if (record.show()) {
+ int userId = UserHandle.getUserId(record.uid);
+ boolean rateLimitingEnabled =
+ CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid);
+ boolean isWithinQuota =
+ mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG);
+ if ((!rateLimitingEnabled || isWithinQuota) && record.show()) {
scheduleDurationReachedLocked(record);
mIsCurrentToastShown = true;
+ if (rateLimitingEnabled) {
+ mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG);
+ }
return;
+ } else if (rateLimitingEnabled && !isWithinQuota) {
+ Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the "
+ + "following toast was blocked and discarded: " + record);
}
int index = mToastQueue.indexOf(record);
if (index >= 0) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 1c0349d1b51f..cbd973aeaf6a 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -52,6 +52,7 @@ import android.service.notification.RankingHelperProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -170,6 +171,8 @@ public class PreferencesHelper implements RankingConfig {
private final AppOpsManager mAppOps;
private SparseBooleanArray mBadgingEnabled;
+ private SparseBooleanArray mLockScreenShowNotifications;
+ private SparseBooleanArray mLockScreenPrivateNotifications;
private boolean mBubblesEnabledGlobally = DEFAULT_GLOBAL_ALLOW_BUBBLE;
private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING;
private boolean mAreChannelsBypassingDnd;
@@ -1379,36 +1382,39 @@ public class PreferencesHelper implements RankingConfig {
return null;
}
- public ArrayList<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
+ public ArrayList<ConversationChannelWrapper> getConversations(IntArray userIds,
+ boolean onlyImportant) {
synchronized (mPackagePreferences) {
ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
-
for (PackagePreferences p : mPackagePreferences.values()) {
- int N = p.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = p.channels.valueAt(i);
- if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
- && !nc.isDemoted()
- && (nc.isImportantConversation() || !onlyImportant)) {
- ConversationChannelWrapper conversation = new ConversationChannelWrapper();
- conversation.setPkg(p.pkg);
- conversation.setUid(p.uid);
- conversation.setNotificationChannel(nc);
- conversation.setParentChannelLabel(
- p.channels.get(nc.getParentChannelId()).getName());
- boolean blockedByGroup = false;
- if (nc.getGroup() != null) {
- NotificationChannelGroup group = p.groups.get(nc.getGroup());
- if (group != null) {
- if (group.isBlocked()) {
- blockedByGroup = true;
- } else {
- conversation.setGroupLabel(group.getName());
+ if (userIds.binarySearch(UserHandle.getUserId(p.uid)) >= 0) {
+ int N = p.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = p.channels.valueAt(i);
+ if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()
+ && !nc.isDemoted()
+ && (nc.isImportantConversation() || !onlyImportant)) {
+ ConversationChannelWrapper conversation =
+ new ConversationChannelWrapper();
+ conversation.setPkg(p.pkg);
+ conversation.setUid(p.uid);
+ conversation.setNotificationChannel(nc);
+ conversation.setParentChannelLabel(
+ p.channels.get(nc.getParentChannelId()).getName());
+ boolean blockedByGroup = false;
+ if (nc.getGroup() != null) {
+ NotificationChannelGroup group = p.groups.get(nc.getGroup());
+ if (group != null) {
+ if (group.isBlocked()) {
+ blockedByGroup = true;
+ } else {
+ conversation.setGroupLabel(group.getName());
+ }
}
}
- }
- if (!blockedByGroup) {
- conversations.add(conversation);
+ if (!blockedByGroup) {
+ conversations.add(conversation);
+ }
}
}
}
@@ -2401,6 +2407,60 @@ public class PreferencesHelper implements RankingConfig {
return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
}
+ public void updateLockScreenPrivateNotifications() {
+ if (mLockScreenPrivateNotifications == null) {
+ mLockScreenPrivateNotifications = new SparseBooleanArray();
+ }
+ boolean changed = false;
+ // update the cached values
+ for (int index = 0; index < mLockScreenPrivateNotifications.size(); index++) {
+ int userId = mLockScreenPrivateNotifications.keyAt(index);
+ final boolean oldValue = mLockScreenPrivateNotifications.get(userId);
+ final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, userId) != 0;
+ mLockScreenPrivateNotifications.put(userId, newValue);
+ changed |= oldValue != newValue;
+ }
+ if (changed) {
+ updateConfig();
+ }
+ }
+
+ public void updateLockScreenShowNotifications() {
+ if (mLockScreenShowNotifications == null) {
+ mLockScreenShowNotifications = new SparseBooleanArray();
+ }
+ boolean changed = false;
+ // update the cached values
+ for (int index = 0; index < mLockScreenShowNotifications.size(); index++) {
+ int userId = mLockScreenShowNotifications.keyAt(index);
+ final boolean oldValue = mLockScreenShowNotifications.get(userId);
+ final boolean newValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, userId) != 0;
+ mLockScreenShowNotifications.put(userId, newValue);
+ changed |= oldValue != newValue;
+ }
+ if (changed) {
+ updateConfig();
+ }
+ }
+
+ @Override
+ public boolean canShowNotificationsOnLockscreen(int userId) {
+ if (mLockScreenShowNotifications == null) {
+ mLockScreenShowNotifications = new SparseBooleanArray();
+ }
+ return mLockScreenShowNotifications.get(userId, true);
+ }
+
+ @Override
+ public boolean canShowPrivateNotificationsOnLockScreen(int userId) {
+ if (mLockScreenPrivateNotifications == null) {
+ mLockScreenPrivateNotifications = new SparseBooleanArray();
+ }
+ return mLockScreenPrivateNotifications.get(userId, true);
+ }
+
public void unlockAllNotificationChannels() {
synchronized (mPackagePreferences) {
final int numPackagePreferences = mPackagePreferences.size();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b2827bae40bc..8991ced21fbd 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -34,6 +34,8 @@ public interface RankingConfig {
/** Returns true when feature is enabled that shows media notifications in quick settings. */
boolean isMediaNotificationFilteringEnabled();
boolean isGroupBlocked(String packageName, int uid, String groupId);
+ boolean canShowNotificationsOnLockscreen(int userId);
+ boolean canShowPrivateNotificationsOnLockScreen(int userId);
Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid);
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index 9c3d6d352c89..b0be20698005 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_PERSONS_DATA;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
@@ -192,8 +193,8 @@ public class ShortcutHelper {
LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
query.setPackage(packageName);
query.setShortcutIds(Arrays.asList(shortcutId));
- query.setQueryFlags(
- FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER | FLAG_MATCH_CACHED);
+ query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
+ | FLAG_MATCH_CACHED | FLAG_GET_PERSONS_DATA);
List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
? shortcuts.get(0)
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index b5ca2abf8afe..2122b9c19805 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -558,8 +558,8 @@ public class SnoozeHelper {
if (value < currentTime) {
return;
}
- out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME,
- value.toString());
+ out.attributeLong(null, XML_SNOOZED_NOTIFICATION_TIME,
+ value);
});
writeXml(out, mPersistedSnoozedNotificationsWithContext,
XML_SNOOZED_NOTIFICATION_CONTEXT,
diff --git a/services/core/java/com/android/server/notification/VisibilityExtractor.java b/services/core/java/com/android/server/notification/VisibilityExtractor.java
index db548465d4b8..a363601b56e9 100644
--- a/services/core/java/com/android/server/notification/VisibilityExtractor.java
+++ b/services/core/java/com/android/server/notification/VisibilityExtractor.java
@@ -15,8 +15,11 @@
*/
package com.android.server.notification;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.service.notification.NotificationListenerService;
+import android.os.UserHandle;
import android.util.Slog;
/**
@@ -27,9 +30,11 @@ public class VisibilityExtractor implements NotificationSignalExtractor {
private static final boolean DBG = false;
private RankingConfig mConfig;
+ private DevicePolicyManager mDpm;
public void initialize(Context ctx, NotificationUsageStats usageStats) {
if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ mDpm = ctx.getSystemService(DevicePolicyManager.class);
}
public RankingReconsideration process(NotificationRecord record) {
@@ -43,7 +48,38 @@ public class VisibilityExtractor implements NotificationSignalExtractor {
return null;
}
- record.setPackageVisibilityOverride(record.getChannel().getLockscreenVisibility());
+ int userId = record.getUserId();
+
+ if (userId == UserHandle.USER_ALL) {
+ record.setPackageVisibilityOverride(record.getChannel().getLockscreenVisibility());
+ } else {
+ boolean userCanShowNotifications =
+ mConfig.canShowNotificationsOnLockscreen(userId);
+ boolean dpmCanShowNotifications = adminAllowsKeyguardFeature(userId,
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+ boolean channelCanShowNotifications = record.getChannel().getLockscreenVisibility()
+ != Notification.VISIBILITY_SECRET;
+
+ if (!userCanShowNotifications || !dpmCanShowNotifications
+ || !channelCanShowNotifications) {
+ record.setPackageVisibilityOverride(Notification.VISIBILITY_SECRET);
+ } else {
+ // notifications are allowed but should they be redacted?
+
+ boolean userCanShowContents =
+ mConfig.canShowPrivateNotificationsOnLockScreen(userId);
+ boolean dpmCanShowContents = adminAllowsKeyguardFeature(userId,
+ DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+ boolean channelCanShowContents = record.getChannel().getLockscreenVisibility()
+ != Notification.VISIBILITY_PRIVATE;
+
+ if (!userCanShowContents || !dpmCanShowContents || !channelCanShowContents) {
+ record.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE);
+ } else {
+ record.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ }
+ }
+ }
return null;
}
@@ -57,4 +93,13 @@ public class VisibilityExtractor implements NotificationSignalExtractor {
public void setZenHelper(ZenModeHelper helper) {
}
+
+ private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
+ if (userHandle == UserHandle.USER_ALL) {
+ return true;
+ }
+ final int dpmFlags = mDpm.getKeyguardDisabledFeatures(null /* admin */, userHandle);
+ return (dpmFlags & feature) == 0;
+ }
+
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 94f46ba6bc60..5cd22e0bd72d 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -81,6 +81,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import libcore.io.IoUtils;
@@ -1165,7 +1166,7 @@ public class ZenModeHelper {
try {
parser = resources.getXml(R.xml.default_zen_mode_config);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
- final ZenModeConfig config = ZenModeConfig.readXml(parser);
+ final ZenModeConfig config = ZenModeConfig.readXml(XmlUtils.makeTyped(parser));
if (config != null) return config;
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/om/TEST_MAPPING b/services/core/java/com/android/server/om/TEST_MAPPING
index 6edd76f1810a..e8a2a02e6a8c 100644
--- a/services/core/java/com/android/server/om/TEST_MAPPING
+++ b/services/core/java/com/android/server/om/TEST_MAPPING
@@ -15,9 +15,6 @@
"name": "OverlayHostTests"
},
{
- "name": "OverlayRemountedTest"
- },
- {
"name": "CtsAppSecurityHostTestCases",
"options": [
{
@@ -25,5 +22,10 @@
}
]
}
+ ],
+ "presubmit-large": [
+ {
+ "name": "OverlayRemountedTest"
+ }
]
}
diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java
new file mode 100644
index 000000000000..a17967fcc76e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.role.RoleManager;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.FgThread;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Interacts with {@link RoleManager} to provide and manage default apps.
+ */
+public class DefaultAppProvider {
+ @NonNull
+ private final Supplier<RoleManager> mRoleManagerSupplier;
+
+ /**
+ * Create a new instance of this class
+ *
+ * @param roleManagerSupplier the supplier for {@link RoleManager}
+ */
+ public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) {
+ mRoleManagerSupplier = roleManagerSupplier;
+ }
+
+ /**
+ * Get the package name of the default browser.
+ *
+ * @param userId the user ID
+ * @return the package name of the default browser, or {@code null} if none
+ */
+ @Nullable
+ public String getDefaultBrowser(@UserIdInt int userId) {
+ return getRoleHolder(RoleManager.ROLE_BROWSER, userId);
+ }
+
+ /**
+ * Set the package name of the default browser.
+ *
+ * @param packageName package name of the default browser, or {@code null} to unset
+ * @param async whether the operation should be asynchronous
+ * @param userId the user ID
+ * @return whether the default browser was successfully set.
+ */
+ public boolean setDefaultBrowser(@Nullable String packageName, boolean async,
+ @UserIdInt int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ return false;
+ }
+ final RoleManager roleManager = mRoleManagerSupplier.get();
+ if (roleManager == null) {
+ return false;
+ }
+ final UserHandle user = UserHandle.of(userId);
+ final Executor executor = FgThread.getExecutor();
+ final AndroidFuture<Void> future = new AndroidFuture<>();
+ final Consumer<Boolean> callback = successful -> {
+ if (successful) {
+ future.complete(null);
+ } else {
+ future.completeExceptionally(new RuntimeException());
+ }
+ };
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (packageName != null) {
+ roleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, user,
+ executor, callback);
+ } else {
+ roleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor,
+ callback);
+ }
+ if (!async) {
+ try {
+ future.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.e(PackageManagerService.TAG, "Exception while setting default browser: "
+ + packageName, e);
+ return false;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return true;
+ }
+
+ /**
+ * Get the package name of the default dialer.
+ *
+ * @param userId the user ID
+ * @return the package name of the default dialer, or {@code null} if none
+ */
+ @Nullable
+ public String getDefaultDialer(@NonNull int userId) {
+ return getRoleHolder(RoleManager.ROLE_DIALER, userId);
+ }
+
+ /**
+ * Get the package name of the default home.
+ *
+ * @param userId the user ID
+ * @return the package name of the default home, or {@code null} if none
+ */
+ @Nullable
+ public String getDefaultHome(@NonNull int userId) {
+ return getRoleHolder(RoleManager.ROLE_HOME, userId);
+ }
+
+ /**
+ * Set the package name of the default home.
+ *
+ * @param packageName package name of the default home
+ * @param userId the user ID
+ * @param executor the {@link Executor} to execute callback on
+ * @param callback the callback made after the default home as been updated
+ * @return whether the default home was set
+ */
+ public boolean setDefaultHome(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
+ final RoleManager roleManager = mRoleManagerSupplier.get();
+ if (roleManager == null) {
+ return false;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, packageName, 0,
+ UserHandle.of(userId), executor, callback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return true;
+ }
+
+ @Nullable
+ private String getRoleHolder(@NonNull String roleName, @NonNull int userId) {
+ final RoleManager roleManager = mRoleManagerSupplier.get();
+ if (roleManager == null) {
+ return null;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(roleName,
+ UserHandle.of(userId)));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index 30b1c2c93a45..24a3e520fe1b 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -55,7 +55,7 @@ public interface PackageAbiHelper {
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp,
- String cpuAbiOverride) throws PackageManagerException;
+ String cpuAbiOverride, File appLib32InstallDir) throws PackageManagerException;
/**
* Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index da4ea16d0bfd..71b99bd4ced2 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -296,7 +296,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
@Override
public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg,
- boolean isUpdatedSystemApp, String cpuAbiOverride)
+ boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)
throws PackageManagerException {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
@@ -304,7 +304,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg);
final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths(
new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
- PackageManagerService.sAppLib32InstallDir, pkg.getPath(),
+ appLib32InstallDir, pkg.getPath(),
pkg.getBaseApkPath(), pkg.isSystem(),
isUpdatedSystemApp);
@@ -452,7 +452,7 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
return new Pair<>(abis,
- deriveNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
+ deriveNativeLibraryPaths(abis, appLib32InstallDir,
pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(),
isUpdatedSystemApp));
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c10d394d6344..0aebe721b103 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -31,22 +31,16 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageParser.APEX_FILE_EXTENSION;
-import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
import static com.android.internal.util.XmlUtils.readBitmapAttribute;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
import static com.android.internal.util.XmlUtils.readUriAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.internal.util.XmlUtils.writeUriAttribute;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
@@ -110,6 +104,7 @@ import android.os.ParcelableException;
import android.os.Process;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
+import android.os.SELinux;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.incremental.IStorageHealthListener;
@@ -156,7 +151,6 @@ import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -483,6 +477,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private IncrementalFileStorages mIncrementalFileStorages;
+ @GuardedBy("mLock")
+ private PackageLite mPackageLite;
+
private static final FileFilter sAddedApkFilter = new FileFilter() {
@Override
public boolean accept(File file) {
@@ -1092,6 +1089,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ @Override
+ public void stageViaHardLink(String path) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("link() can only be run by the system");
+ }
+
+ try {
+ final File target = new File(path);
+ final File source = new File(stageDir, target.getName());
+ try {
+ Os.link(path, source.getAbsolutePath());
+ // Grant READ access for APK to be read successfully
+ Os.chmod(source.getAbsolutePath(), 0644);
+ } catch (ErrnoException e) {
+ e.rethrowAsIOException();
+ }
+ if (!SELinux.restorecon(source)) {
+ throw new IOException("Can't relabel file: " + source);
+ }
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode)
throws IOException, ErrnoException {
// TODO: this should delegate to DCS so the system process avoids
@@ -2040,7 +2062,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// TODO(b/136257624): Some logic in this if block probably belongs in
// makeInstallParams().
- if (!params.isMultiPackage && !isApexSession()) {
+ if (!isMultiPackage() && !isApexSession()) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
@@ -2094,12 +2116,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Failed to inherit existing install", e);
}
}
+ // For mode inherit existing, it would link/copy existing files to stage dir in the
+ // above block. Therefore, we need to parse the complete package in stage dir here.
+ // Besides, PackageLite may be null for staged sessions that don't complete pre-reboot
+ // verification.
+ mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
// TODO: surface more granular state from dexopt
mInternalProgress = 0.5f;
computeProgressLocked(true);
- extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
+ extractNativeLibraries(mPackageLite, stageDir, params.abiOverride,
+ mayInheritNativeLibs());
}
final IPackageInstallObserver2 localObserver;
@@ -2142,7 +2170,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
}
return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,
- mInstallSource, mInstallerUid, mSigningDetails, sessionId);
+ mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite);
}
private void onVerificationComplete() {
@@ -2215,10 +2243,37 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
params.installFlags |= INSTALL_STAGED;
}
+ if (!isMultiPackage() && !isApexSession()) {
+ synchronized (mLock) {
+ // This shouldn't be null, but have this code path just in case.
+ if (mPackageLite == null) {
+ Slog.wtf(TAG, "Session: " + sessionId + ". Don't have a valid PackageLite.");
+ }
+ mPackageLite = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
+ }
+ }
+
synchronized (mLock) {
return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,
- mSigningDetails, mInstallerUid);
+ mSigningDetails, mInstallerUid, mPackageLite);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private PackageLite getOrParsePackageLiteLocked(File packageFile, int flags)
+ throws PackageManagerException {
+ if (mPackageLite != null) {
+ return mPackageLite;
+ }
+
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<PackageLite> result =
+ ApkLiteParseUtils.parsePackageLite(input, packageFile, flags);
+ if (result.isError()) {
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+ result.getErrorMessage(), result.getException());
}
+ return result.getResult();
}
private static void maybeRenameFile(File from, File to) throws PackageManagerException {
@@ -2366,13 +2421,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private static String splitNameToFileName(String splitName) {
- if (splitName == null) {
- return "base";
- }
- return "split_" + splitName;
- }
-
/**
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
@@ -2388,6 +2436,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private void validateApkInstallLocked() throws PackageManagerException {
ApkLite baseApk = null;
+ PackageLite packageLite = null;
+ mPackageLite = null;
mPackageName = null;
mVersionCode = -1;
mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
@@ -2431,6 +2481,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
+ final ArrayMap<String, PackageParser.ApkLite> splitApks = new ArrayMap<>();
ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (File addedFile : addedFiles) {
ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
@@ -2458,8 +2509,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertApkConsistentLocked(String.valueOf(addedFile), apk);
// Take this opportunity to enforce uniform naming
- final String fileName = splitNameToFileName(apk.splitName);
- final String targetName = fileName + APK_FILE_EXTENSION;
+ final String targetName = ApkLiteParseUtils.splitNameToFileName(apk);
if (!FileUtils.isValidExtFilename(targetName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Invalid filename: " + targetName);
@@ -2483,6 +2533,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (apk.splitName == null) {
mResolvedBaseFile = targetFile;
baseApk = apk;
+ } else {
+ splitApks.put(apk.splitName, apk);
}
}
@@ -2506,14 +2558,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mVersionCode = pkgInfo.getLongVersionCode();
}
if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
- try {
- mSigningDetails = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- pkgInfo.applicationInfo.sourceDir,
- PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
- } catch (PackageParserException e) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Couldn't obtain signatures from base APK");
- }
+ mSigningDetails = unsafeGetCertsWithoutVerification(
+ pkgInfo.applicationInfo.sourceDir);
}
}
@@ -2548,6 +2594,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
"Missing split for " + mPackageName);
}
+ // For mode full install, we compose package lite for future usage instead of
+ // re-parsing it again and again.
+ final ParseResult<PackageLite> pkgLiteResult =
+ ApkLiteParseUtils.composePackageLiteFromApks(input.reset(), stageDir, baseApk,
+ splitApks, true);
+ if (pkgLiteResult.isError()) {
+ throw new PackageManagerException(pkgLiteResult.getErrorCode(),
+ pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
+ }
+ mPackageLite = pkgLiteResult.getResult();
+ packageLite = mPackageLite;
} else {
final ApplicationInfo appInfo = pkgInfo.applicationInfo;
ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite(
@@ -2557,22 +2614,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
}
final PackageLite existing = pkgLiteResult.getResult();
- ParseResult<ApkLite> apkLiteResult = ApkLiteParseUtils.parseApkLite(input.reset(),
- new File(appInfo.getBaseCodePath()),
- PackageParser.PARSE_COLLECT_CERTIFICATES);
- if (apkLiteResult.isError()) {
- throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
- apkLiteResult.getErrorMessage(), apkLiteResult.getException());
+ packageLite = existing;
+ assertPackageConsistentLocked("Existing", existing.packageName,
+ existing.getLongVersionCode());
+ final PackageParser.SigningDetails signingDetails =
+ unsafeGetCertsWithoutVerification(existing.baseCodePath);
+ if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Existing signatures are inconsistent");
}
- final ApkLite existingBase = apkLiteResult.getResult();
-
- assertApkConsistentLocked("Existing base", existingBase);
// Inherit base if not overridden.
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
inheritFileLocked(mResolvedBaseFile);
- baseApk = existingBase;
}
// Inherit splits if not overridden.
@@ -2659,7 +2714,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
// For the case of split required, failed if no splits existed
- if (baseApk.isSplitRequired) {
+ if (packageLite.isSplitRequired) {
final int existingSplits = ArrayUtils.size(existing.splitNames);
final boolean allSplitsRemoved = (existingSplits == removeSplitList.size());
final boolean onlyBaseFileStaged = (stagedSplits.size() == 1
@@ -2670,7 +2725,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
}
- if (baseApk.useEmbeddedDex) {
+ if (packageLite.useEmbeddedDex) {
for (File file : mResolvedStagedFiles) {
if (file.getName().endsWith(".apk")
&& !DexManager.auditUncompressedDexInApk(file.getPath())) {
@@ -2683,7 +2738,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
- if (!baseApk.debuggable && !baseApk.profilableByShell) {
+ if (!packageLite.debuggable && !packageLite.profilableByShell) {
mIncrementalFileStorages.disableReadLogs();
}
}
@@ -2838,23 +2893,40 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private void assertApkConsistentLocked(String tag, ApkLite apk)
throws PackageManagerException {
- if (!mPackageName.equals(apk.packageName)) {
+ assertPackageConsistentLocked(tag, apk.packageName, apk.getLongVersionCode());
+ if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ tag + " signatures are inconsistent");
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void assertPackageConsistentLocked(String tag, String packageName,
+ long versionCode) throws PackageManagerException {
+ if (!mPackageName.equals(packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
- + apk.packageName + " inconsistent with " + mPackageName);
+ + packageName + " inconsistent with " + mPackageName);
}
- if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
+ if (params.appPackageName != null && !params.appPackageName.equals(packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
+ " specified package " + params.appPackageName
- + " inconsistent with " + apk.packageName);
+ + " inconsistent with " + packageName);
}
- if (mVersionCode != apk.getLongVersionCode()) {
+ if (mVersionCode != versionCode) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
- + " version code " + apk.versionCode + " inconsistent with "
+ + " version code " + versionCode + " inconsistent with "
+ mVersionCode);
}
- if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
+ }
+
+ private PackageParser.SigningDetails unsafeGetCertsWithoutVerification(String path)
+ throws PackageManagerException {
+ try {
+ return ApkSignatureVerifier.unsafeGetCertsWithoutVerification(path,
+ PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
+ } catch (PackageParserException e) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- tag + " signatures are inconsistent");
+ "Couldn't obtain signatures from APK : " + path);
}
}
@@ -2989,8 +3061,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
}
- private void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
+ private void extractNativeLibraries(PackageLite packageLite, File packageDir,
+ String abiOverride, boolean inherit)
throws PackageManagerException {
+ Objects.requireNonNull(packageLite);
final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
if (!inherit) {
// Start from a clean slate
@@ -2999,7 +3073,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
NativeLibraryHelper.Handle handle = null;
try {
- handle = NativeLibraryHelper.Handle.create(packageDir);
+ handle = NativeLibraryHelper.Handle.create(packageLite);
final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
abiOverride, isIncrementalInstallation());
if (res != INSTALL_SUCCEEDED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 85659edd1321..a123f369a9ab 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -152,6 +152,7 @@ import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
+import android.app.role.RoleManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
@@ -379,6 +380,8 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerService;
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -448,7 +451,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.function.Supplier;
/**
* Keep track of all those APKs everywhere.
@@ -732,8 +734,6 @@ public class PackageManagerService extends IPackageManager.Stub
private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
- final ServiceThread mHandlerThread;
-
final Handler mHandler;
private final ProcessLoggingHandler mProcessLoggingHandler;
@@ -761,12 +761,14 @@ public class PackageManagerService extends IPackageManager.Stub
final Installer mInstaller;
/** Directory where installed applications are stored */
- private static final File sAppInstallDir =
- new File(Environment.getDataDirectory(), "app");
+ private final File mAppInstallDir;
/** Directory where installed application's 32-bit native libraries are copied. */
@VisibleForTesting
- static final File sAppLib32InstallDir =
- new File(Environment.getDataDirectory(), "app-lib");
+ final File mAppLib32InstallDir;
+
+ private static File getAppLib32InstallDir() {
+ return new File(Environment.getDataDirectory(), "app-lib");
+ }
// ----------------------------------------------------------------
@@ -832,6 +834,11 @@ public class PackageManagerService extends IPackageManager.Stub
boolean mFirstBoot;
+ private final boolean mIsEngBuild;
+ private final boolean mIsUserDebugBuild;
+ private final String mIncrementalVersion;
+
+
PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
@GuardedBy("mAvailableFeatures")
@@ -866,6 +873,8 @@ public class PackageManagerService extends IPackageManager.Stub
private final Injector mInjector;
+ private final SystemWrapper mSystemWrapper;
+
/**
* The list of all system partitions that may contain packages in ascending order of
* specificity (the more generic, the earlier in the list a partition appears).
@@ -896,6 +905,10 @@ public class PackageManagerService extends IPackageManager.Stub
T produce(Injector injector, PackageManagerService packageManager);
}
+ interface ProducerWithArgument<T, R> {
+ T produce(Injector injector, PackageManagerService packageManager, R argument);
+ }
+
interface ServiceProducer {
<T> T produce(Class<T> c);
}
@@ -924,6 +937,8 @@ public class PackageManagerService extends IPackageManager.Stub
private final Object mInstallLock;
private final Handler mBackgroundHandler;
private final Executor mBackgroundExecutor;
+ private final List<ScanPartition> mSystemPartitions;
+
// ----- producers -----
private final Singleton<ComponentResolver> mComponentResolverProducer;
@@ -940,13 +955,25 @@ public class PackageManagerService extends IPackageManager.Stub
private final Singleton<ViewCompiler> mViewCompilerProducer;
private final Singleton<IPermissionManager> mPermissionManagerProducer;
private final Singleton<IncrementalManager> mIncrementalManagerProducer;
+ private final Singleton<DefaultAppProvider> mDefaultAppProviderProducer;
+ private final Singleton<DisplayMetrics> mDisplayMetricsProducer;
+ private final Producer<PackageParser2> mScanningCachingPackageParserProducer;
+ private final Producer<PackageParser2> mScanningPackageParserProducer;
+ private final Producer<PackageParser2> mPreparingPackageParserProducer;
+ private final Singleton<PackageInstallerService> mPackageInstallerServiceProducer;
+ private final ProducerWithArgument<InstantAppResolverConnection, ComponentName>
+ mInstantAppResolverConnectionProducer;
+ private final Singleton<LegacyPermissionManagerInternal>
+ mLegacyPermissionManagerInternalProducer;
private final SystemWrapper mSystemWrapper;
private final ServiceProducer mGetLocalServiceProducer;
private final ServiceProducer mGetSystemServiceProducer;
+ private final Singleton<ModuleInfoProvider> mModuleInfoProviderProducer;
Injector(Context context, Object lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
Handler backgroundHandler,
+ List<ScanPartition> systemPartitions,
Producer<ComponentResolver> componentResolverProducer,
Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer,
Producer<UserManagerService> userManagerProducer,
@@ -961,6 +988,16 @@ public class PackageManagerService extends IPackageManager.Stub
Producer<IPermissionManager> permissionManagerProducer,
Producer<ViewCompiler> viewCompilerProducer,
Producer<IncrementalManager> incrementalManagerProducer,
+ Producer<DefaultAppProvider> defaultAppProviderProducer,
+ Producer<DisplayMetrics> displayMetricsProducer,
+ Producer<PackageParser2> scanningCachingPackageParserProducer,
+ Producer<PackageParser2> scanningPackageParserProducer,
+ Producer<PackageParser2> preparingPackageParserProducer,
+ Producer<PackageInstallerService> packageInstallerServiceProducer,
+ ProducerWithArgument<InstantAppResolverConnection, ComponentName>
+ instantAppResolverConnectionProducer,
+ Producer<ModuleInfoProvider> moduleInfoProviderProducer,
+ Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer,
SystemWrapper systemWrapper,
ServiceProducer getLocalServiceProducer,
ServiceProducer getSystemServiceProducer) {
@@ -971,6 +1008,7 @@ public class PackageManagerService extends IPackageManager.Stub
mInstallLock = installLock;
mBackgroundHandler = backgroundHandler;
mBackgroundExecutor = new HandlerExecutor(backgroundHandler);
+ mSystemPartitions = systemPartitions;
mComponentResolverProducer = new Singleton<>(componentResolverProducer);
mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer);
mUserManagerProducer = new Singleton<>(userManagerProducer);
@@ -985,6 +1023,16 @@ public class PackageManagerService extends IPackageManager.Stub
mPermissionManagerProducer = new Singleton<>(permissionManagerProducer);
mViewCompilerProducer = new Singleton<>(viewCompilerProducer);
mIncrementalManagerProducer = new Singleton<>(incrementalManagerProducer);
+ mDefaultAppProviderProducer = new Singleton<>(defaultAppProviderProducer);
+ mDisplayMetricsProducer = new Singleton<>(displayMetricsProducer);
+ mScanningCachingPackageParserProducer = scanningCachingPackageParserProducer;
+ mScanningPackageParserProducer = scanningPackageParserProducer;
+ mPreparingPackageParserProducer = preparingPackageParserProducer;
+ mPackageInstallerServiceProducer = new Singleton<>(packageInstallerServiceProducer);
+ mInstantAppResolverConnectionProducer = instantAppResolverConnectionProducer;
+ mModuleInfoProviderProducer = new Singleton<>(moduleInfoProviderProducer);
+ mLegacyPermissionManagerInternalProducer = new Singleton<>(
+ legacyPermissionManagerInternalProducer);
mSystemWrapper = systemWrapper;
mGetLocalServiceProducer = getLocalServiceProducer;
mGetSystemServiceProducer = getSystemServiceProducer;
@@ -1010,6 +1058,10 @@ public class PackageManagerService extends IPackageManager.Stub
return mInstallLock;
}
+ public List<ScanPartition> getSystemPartitions() {
+ return mSystemPartitions;
+ }
+
public UserManagerService getUserManagerService() {
return mUserManagerProducer.get(this, mPackageManager);
}
@@ -1082,6 +1134,10 @@ public class PackageManagerService extends IPackageManager.Stub
return mBackgroundExecutor;
}
+ public DisplayMetrics getDisplayMetrics() {
+ return mDisplayMetricsProducer.get(this, mPackageManager);
+ }
+
public <T> T getLocalService(Class<T> c) {
return mGetLocalServiceProducer.produce(c);
}
@@ -1097,53 +1153,67 @@ public class PackageManagerService extends IPackageManager.Stub
public IncrementalManager getIncrementalManager() {
return mIncrementalManagerProducer.get(this, mPackageManager);
}
- }
- /** Provides an abstraction to static access to system state. */
- public interface SystemWrapper {
- /** @see SystemProperties#get(String) */
- String getProperty(String key);
- /** @see SystemProperties#getInt(String, int) */
- int getPropertyInt(String key, int defValue);
- /** @see SystemProperties#getBoolean(String, boolean) */
- boolean getPropertyBoolean(String key, boolean defValue);
- /** @see SystemProperties#digestOf(String...) */
- String digestOfProperties(@NonNull String... keys);
- /** @see SystemProperties#set(String, String) */
- void setProperty(String key, String value);
- /** @see Build.VERSION#SDK_INT */
- int getSdkInt();
- }
+ public DefaultAppProvider getDefaultAppProvider() {
+ return mDefaultAppProviderProducer.get(this, mPackageManager);
+ }
- private static class DefaultSystemWrapper implements SystemWrapper {
- @Override
- public String getProperty(String key) {
- return SystemProperties.get(key);
+ public PackageParser2 getScanningCachingPackageParser() {
+ return mScanningCachingPackageParserProducer.produce(this, mPackageManager);
+ }
+ public PackageParser2 getScanningPackageParser() {
+ return mScanningPackageParserProducer.produce(this, mPackageManager);
+ }
+ public PackageParser2 getPreparingPackageParser() {
+ return mPreparingPackageParserProducer.produce(this, mPackageManager);
}
- @Override
- public int getPropertyInt(String key, int defValue) {
- return SystemProperties.getInt(key, defValue);
+ public PackageInstallerService getPackageInstallerService() {
+ return mPackageInstallerServiceProducer.get(this, mPackageManager);
}
- @Override
- public boolean getPropertyBoolean(String key, boolean defValue) {
- return SystemProperties.getBoolean(key, defValue);
+ public InstantAppResolverConnection getInstantAppResolverConnection(
+ ComponentName instantAppResolverComponent) {
+ return mInstantAppResolverConnectionProducer.produce(
+ this, mPackageManager, instantAppResolverComponent);
}
- @Override
- public String digestOfProperties(String... keys) {
- return SystemProperties.digestOf(keys);
+ public ModuleInfoProvider getModuleInfoProvider() {
+ return mModuleInfoProviderProducer.get(this, mPackageManager);
}
+ public LegacyPermissionManagerInternal getLegacyPermissionManagerInternal() {
+ return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager);
+ }
+ }
+
+ /** Provides an abstraction to static access to system state. */
+ public interface SystemWrapper {
+ void disablePackageCaches();
+ void enablePackageCaches();
+ }
+
+ private static class DefaultSystemWrapper implements SystemWrapper {
+
@Override
- public void setProperty(String key, String value) {
- SystemProperties.set(key, value);
+ public void disablePackageCaches() {
+ // disable all package caches that shouldn't apply within system server
+ PackageManager.disableApplicationInfoCache();
+ PackageManager.disablePackageInfoCache();
+ ApplicationPackageManager.invalidateGetPackagesForUidCache();
+ ApplicationPackageManager.disableGetPackagesForUidCache();
+ ApplicationPackageManager.invalidateHasSystemFeatureCache();
+
+ // Avoid invalidation-thrashing by preventing cache invalidations from causing property
+ // writes if the cache isn't enabled yet. We re-enable writes later when we're
+ // done initializing.
+ PackageManager.corkPackageInfoCache();
}
@Override
- public int getSdkInt() {
- return Build.VERSION.SDK_INT;
+ public void enablePackageCaches() {
+ // Uncork cache invalidations and allow clients to cache package information.
+ PackageManager.uncorkPackageInfoCache();
}
}
@@ -1154,13 +1224,13 @@ public class PackageManagerService extends IPackageManager.Stub
public ArtManagerService artManagerService;
public @Nullable String configuratorPackage;
public int defParseFlags;
+ public DefaultAppProvider defaultAppProvider;
public DexManager dexManager;
public List<ScanPartition> dirsToScanAsSystem;
public @Nullable String documenterPackage;
public boolean factoryTest;
public ArrayMap<String, FeatureInfo> availableFeatures;
public Handler handler;
- public ServiceThread handlerThread;
public @Nullable String incidentReportApproverPackage;
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
@@ -1173,6 +1243,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isPreNupgrade;
public boolean isPreQupgrade;
public boolean isUpgrade;
+ public LegacyPermissionManagerInternal legacyPermissionManagerInternal;
public DisplayMetrics Metrics;
public ModuleInfoProvider moduleInfoProvider;
public MoveCallbacks moveCallbacks;
@@ -1206,6 +1277,13 @@ public class PackageManagerService extends IPackageManager.Stub
public ArrayMap<String, AndroidPackage> packages;
public boolean enableFreeCacheV2;
public int sdkVersion;
+ public SystemWrapper systemWrapper;
+ public File appInstallDir;
+ public File appLib32InstallDir;
+ public boolean isEngBuild;
+ public boolean isUserDebugBuild;
+ public int sdkInt = Build.VERSION.SDK_INT;
+ public String incrementalVersion = Build.VERSION.INCREMENTAL;
}
private final AppsFilter mAppsFilter;
@@ -1307,6 +1385,10 @@ public class PackageManagerService extends IPackageManager.Stub
private final IncrementalManager mIncrementalManager;
+ private final DefaultAppProvider mDefaultAppProvider;
+
+ private final LegacyPermissionManagerInternal mLegacyPermissionManager;
+
private final PackageProperty mPackageProperty = new PackageProperty();
private static class IFVerificationParams {
@@ -1738,6 +1820,7 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mOverlayConfigSignaturePackage;
final @Nullable String mRecentsPackage;
+ @GuardedBy("mLock")
private final PackageUsage mPackageUsage = new PackageUsage();
private final CompilerStats mCompilerStats = new CompilerStats();
@@ -1802,7 +1885,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int index = 0; i < size && index < numComponents; index++) {
packages[i] = componentsToBroadcast.keyAt(index);
components[i] = componentsToBroadcast.valueAt(index);
- final PackageSetting ps = mSettings.mPackages.get(packages[i]);
+ final PackageSetting ps = mSettings.getPackageLPr(packages[i]);
uids[i] = (ps != null)
? UserHandle.getUid(packageUserId, ps.appId)
: -1;
@@ -2210,22 +2293,27 @@ public class PackageManagerService extends IPackageManager.Stub
res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
}
+ final PermissionManagerServiceInternal.PackageInstalledParams.Builder
+ permissionParamsBuilder =
+ new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
final List<String> grantedPermissionsList;
if (grantPermissions) {
if (grantedPermissions != null) {
- grantedPermissionsList = Arrays.asList(grantedPermissions);
+ permissionParamsBuilder.setGrantedPermissions(Arrays.asList(
+ grantedPermissions));
} else {
- grantedPermissionsList = res.pkg.getRequestedPermissions();
+ permissionParamsBuilder.setGrantedPermissions(
+ res.pkg.getRequestedPermissions());
}
- } else {
- grantedPermissionsList = Collections.emptyList();
}
- if (allowlistedRestrictedPermissions == null) {
- allowlistedRestrictedPermissions = Collections.emptyList();
+ if (allowlistedRestrictedPermissions != null) {
+ permissionParamsBuilder.setAllowlistedRestrictedPermissions(
+ allowlistedRestrictedPermissions);
}
+ permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
for (final int userId : res.newUsers) {
- mPermissionManager.onPackageInstalled(res.pkg, grantedPermissionsList,
- allowlistedRestrictedPermissions, autoRevokePermissionsMode, userId);
+ mPermissionManager.onPackageInstalled(res.pkg, permissionParamsBuilder.build(),
+ userId);
}
final String installerPackageName =
@@ -2304,7 +2392,7 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
getPackageSettingInternal(res.name, Process.SYSTEM_UID),
- updateUserIds, mSettings.mPackages);
+ updateUserIds, mSettings.getPackagesLocked());
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/,
@@ -2741,6 +2829,7 @@ public class PackageManagerService extends IPackageManager.Stub
Injector injector = new Injector(
context, lock, installer, installLock, new PackageAbiHelperImpl(),
backgroundHandler,
+ SYSTEM_PARTITIONS,
(i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
(i, pm) -> PermissionManagerService.create(context),
(i, pm) -> new UserManagerService(context, pm,
@@ -2762,12 +2851,38 @@ public class PackageManagerService extends IPackageManager.Stub
(i, pm) -> (IPermissionManager) ServiceManager.getService("permissionmgr"),
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
- pm.mContext.getSystemService(Context.INCREMENTAL_SERVICE),
+ i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
+ (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(
+ RoleManager.class)),
+ (i, pm) -> new DisplayMetrics(),
+ (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
+ i.getDisplayMetrics(), pm.mCacheDir,
+ pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
+ (i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
+ i.getDisplayMetrics(), null,
+ pm.mPackageParserCallback) /* scanningPackageParserProducer */,
+ (i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(),
+ null, pm.mPackageParserCallback) /* preparingPackageParserProducer */,
+ // Prepare a supplier of package parser for the staging manager to parse apex file
+ // during the staging installation.
+ (i, pm) -> new PackageInstallerService(
+ i.getContext(), pm, i::getScanningPackageParser),
+ (i, pm, cn) -> new InstantAppResolverConnection(
+ i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
+ (i, pm) -> new ModuleInfoProvider(i.getContext(), pm),
+ (i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService);
- PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
+
+ if (Build.VERSION.SDK_INT <= 0) {
+ Slog.w(TAG, "**** ro.build.version.sdk not set!");
+ }
+
+ PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
+ Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
+ Build.VERSION.INCREMENTAL);
t.traceEnd(); // "create package manager"
final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
@@ -2827,11 +2942,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private static void getDefaultDisplayMetrics(
- DisplayManager displayManager, DisplayMetrics metrics) {
- displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics);
- }
-
/**
* Requests that files preopted on a secondary system partition be copied to the data partition
* if possible. Note that the actual copying of the files is accomplished by init for security
@@ -2841,14 +2951,13 @@ public class PackageManagerService extends IPackageManager.Stub
private static void requestCopyPreoptedFiles(Injector injector) {
final int WAIT_TIME_MS = 100;
final String CP_PREOPT_PROPERTY = "sys.cppreopt";
- if (injector.getSystemWrapper().getPropertyInt("ro.cp_system_other_odex", 0) == 1) {
- injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "requested");
+ if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
+ SystemProperties.set(CP_PREOPT_PROPERTY, "requested");
// We will wait for up to 100 seconds.
final long timeStart = SystemClock.uptimeMillis();
final long timeEnd = timeStart + 100 * 1000;
long timeNow = timeStart;
- while (!injector.getSystemWrapper()
- .getProperty(CP_PREOPT_PROPERTY).equals("finished")) {
+ while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) {
try {
Thread.sleep(WAIT_TIME_MS);
} catch (InterruptedException e) {
@@ -2856,7 +2965,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
timeNow = SystemClock.uptimeMillis();
if (timeNow > timeEnd) {
- injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "timed-out");
+ SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out");
Slog.wtf(TAG, "cppreopt did not finish!");
break;
}
@@ -2933,16 +3042,16 @@ public class PackageManagerService extends IPackageManager.Stub
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
-
mApexManager = testParams.apexManager;
mArtManagerService = testParams.artManagerService;
mAvailableFeatures = testParams.availableFeatures;
mDefParseFlags = testParams.defParseFlags;
+ mDefaultAppProvider = testParams.defaultAppProvider;
+ mLegacyPermissionManager = testParams.legacyPermissionManagerInternal;
mDexManager = testParams.dexManager;
mDirsToScanAsSystem = testParams.dirsToScanAsSystem;
mFactoryTest = testParams.factoryTest;
mHandler = testParams.handler;
- mHandlerThread = testParams.handlerThread;
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
mInstantAppRegistry = testParams.instantAppRegistry;
@@ -2987,49 +3096,46 @@ public class PackageManagerService extends IPackageManager.Stub
mServicesExtensionPackageName = testParams.servicesExtensionPackageName;
mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
-
mResolveComponentName = testParams.resolveComponentName;
mPackages.putAll(testParams.packages);
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
mSdkVersion = testParams.sdkVersion;
- }
-
- public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
- PackageManager.disableApplicationInfoCache();
- PackageManager.disablePackageInfoCache();
- ApplicationPackageManager.invalidateGetPackagesForUidCache();
- ApplicationPackageManager.disableGetPackagesForUidCache();
- ApplicationPackageManager.invalidateHasSystemFeatureCache();
-
- // Avoid invalidation-thrashing by preventing cache invalidations from causing property
- // writes if the cache isn't enabled yet. We re-enable writes later when we're
- // done initializing.
- PackageManager.corkPackageInfoCache();
+ mSystemWrapper = testParams.systemWrapper;
+ mAppInstallDir = testParams.appInstallDir;
+ mAppLib32InstallDir = testParams.appLib32InstallDir;
+ mIsEngBuild = testParams.isEngBuild;
+ mIsUserDebugBuild = testParams.isUserDebugBuild;
+ mIncrementalVersion = testParams.incrementalVersion;
+ }
+
+ public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
+ final String buildFingerprint, final boolean isEngBuild,
+ final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
+ mIsEngBuild = isEngBuild;
+ mIsUserDebugBuild = isUserDebugBuild;
+ mSdkVersion = sdkVersion;
+ mIncrementalVersion = incrementalVersion;
+ mInjector = injector;
+ mInjector.getSystemWrapper().disablePackageCaches();
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
mPendingBroadcasts = new PendingPackageBroadcasts();
- mInjector = injector;
mInjector.bootstrap(this);
mLock = injector.getLock();
mInstallLock = injector.getInstallLock();
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
- mSdkVersion = injector.getSystemWrapper().getSdkInt();
-
- if (mSdkVersion <= 0) {
- Slog.w(TAG, "**** ro.build.version.sdk not set!");
- }
+ mSystemWrapper = injector.getSystemWrapper();
mContext = injector.getContext();
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
- mMetrics = new DisplayMetrics();
+ mMetrics = injector.getDisplayMetrics();
mInstaller = injector.getInstaller();
- mEnableFreeCacheV2 =
- injector.getSystemWrapper().getPropertyBoolean("fw.free_cache_v2", true);
+ mEnableFreeCacheV2 = SystemProperties.getBoolean("fw.free_cache_v2", true);
// Create sub-components that provide services / data. Order here is important.
t.traceBegin("createSubComponents");
@@ -3045,6 +3151,8 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings = injector.getSettings();
mPermissionManagerService = injector.getPermissionManagerService();
mIncrementalManager = mInjector.getIncrementalManager();
+ mDefaultAppProvider = mInjector.getDefaultAppProvider();
+ mLegacyPermissionManager = mInjector.getLegacyPermissionManagerInternal();
PlatformCompat platformCompat = mInjector.getCompatibility();
mPackageParserCallback = new PackageParser2.Callback() {
@Override
@@ -3080,8 +3188,8 @@ public class PackageManagerService extends IPackageManager.Stub
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
t.traceEnd();
- String separateProcesses =
- injector.getSystemWrapper().getProperty("debug.separate_processes");
+ String separateProcesses = SystemProperties.get("debug.separate_processes");
+
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
@@ -3104,7 +3212,8 @@ public class PackageManagerService extends IPackageManager.Stub
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mViewCompiler = injector.getViewCompiler();
- getDefaultDisplayMetrics(mInjector.getSystemService(DisplayManager.class), mMetrics);
+ mContext.getSystemService(DisplayManager.class)
+ .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);
t.traceBegin("get system config");
SystemConfig systemConfig = injector.getSystemConfig();
@@ -3126,18 +3235,21 @@ public class PackageManagerService extends IPackageManager.Stub
}
mDirsToScanAsSystem = new ArrayList<>();
- mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
+ mDirsToScanAsSystem.addAll(injector.getSystemPartitions());
mDirsToScanAsSystem.addAll(scanPartitions);
Slog.d(TAG, "Directories scanned as system partitions: " + mDirsToScanAsSystem);
+ mAppInstallDir = new File(Environment.getDataDirectory(), "app");
+ mAppLib32InstallDir = getAppLib32InstallDir();
+
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
- mHandlerThread = new ServiceThread(TAG,
+ HandlerThread handlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
- mHandlerThread.start();
- mHandler = new PackageHandler(mHandlerThread.getLooper());
+ handlerThread.start();
+ mHandler = new PackageHandler(handlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager);
@@ -3182,12 +3294,13 @@ public class PackageManagerService extends IPackageManager.Stub
// Clean up orphaned packages for which the code path doesn't exist
// and they are an update to a system app - caused by bug/32321269
- final int packageSettingCount = mSettings.mPackages.size();
+ final ArrayMap<String, PackageSetting> packageSettings = mSettings.getPackagesLocked();
+ final int packageSettingCount = packageSettings.size();
for (int i = packageSettingCount - 1; i >= 0; i--) {
- PackageSetting ps = mSettings.mPackages.valueAt(i);
+ PackageSetting ps = packageSettings.valueAt(i);
if (!isExternal(ps) && (ps.getPath() == null || !ps.getPath().exists())
&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
- mSettings.mPackages.removeAt(i);
+ packageSettings.removeAt(i);
mSettings.enableSystemPackageLPw(ps.name);
}
}
@@ -3222,9 +3335,10 @@ public class PackageManagerService extends IPackageManager.Stub
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
final VersionInfo ver = mSettings.getInternalVersion();
- mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
+ mIsUpgrade =
+ !buildFingerprint.equals(ver.fingerprint);
if (mIsUpgrade) {
- logCriticalInfo(Log.INFO,
+ PackageManagerServiceUtils.logCriticalInfo(Log.INFO,
"Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);
}
@@ -3242,13 +3356,13 @@ public class PackageManagerService extends IPackageManager.Stub
// Save the names of pre-existing packages prior to scanning, so we can determine
// which system packages are completely new due to an upgrade.
if (isDeviceUpgrading()) {
- mExistingPackages = new ArraySet<>(mSettings.mPackages.size());
- for (PackageSetting ps : mSettings.mPackages.values()) {
+ mExistingPackages = new ArraySet<>(packageSettings.size());
+ for (PackageSetting ps : packageSettings.values()) {
mExistingPackages.add(ps.name);
}
}
- mCacheDir = preparePackageParserCache(injector);
+ mCacheDir = preparePackageParserCache(mIsEngBuild);
// Set flag to monitor and not change apk file paths when
// scanning install directories.
@@ -3261,8 +3375,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
- PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore,
- mMetrics, mCacheDir, mPackageParserCallback);
+ PackageParser2 packageParser = injector.getScanningCachingPackageParser();
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// Prepare apex package info before scanning APKs, these information are needed when
@@ -3312,6 +3425,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Stub packages must either be replaced with full versions in the /data
// partition or be disabled.
final List<String> stubSystemApps = new ArrayList<>();
+ final int[] userIds = mUserManager.getUserIds();
if (!mOnlyCore) {
// do this first before mucking with mPackages for the "expecting better" case
final int numPackages = mPackages.size();
@@ -3324,8 +3438,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Iterates PackageSettings in reversed order because the item could be removed
// during the iteration.
- for (int index = mSettings.mPackages.size() - 1; index >= 0; index--) {
- final PackageSetting ps = mSettings.mPackages.valueAt(index);
+ for (int index = packageSettings.size() - 1; index >= 0; index--) {
+ final PackageSetting ps = packageSettings.valueAt(index);
/*
* If this is not a system app, it can't be a
@@ -3362,15 +3476,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
- mSettings.mPackages.removeAt(index);
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; it's data will be wiped");
-
- // Assume package is truly gone and wipe residual permissions.
- mPermissionManager.updatePermissions(ps.name, null);
-
- // Actual deletion of code and data will be handled by later
- // reconciliation step
+ removePackageDataLIF(ps, userIds, null, 0, false);
} else {
// we still have a disabled system package, but, it still might have
// been removed. check the code path still exists and check there's
@@ -3412,7 +3520,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
+ scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
packageParser, executorService);
}
@@ -3429,7 +3537,6 @@ public class PackageManagerService extends IPackageManager.Stub
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
- final int[] userIds = mUserManager.getUserIds();
for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
final AndroidPackage pkg = mPackages.get(packageName);
@@ -3470,7 +3577,7 @@ public class PackageManagerService extends IPackageManager.Stub
// previously scanned and known to the system], but, we don't have
// a package [ie. there was an error scanning it from the /data
// partition], completely remove the package data.
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && mPackages.get(packageName) == null) {
removePackageDataLIF(ps, userIds, null, 0, false);
@@ -3596,7 +3703,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Now that we know all the packages we are keeping,
// read and update their last usage times.
- mPackageUsage.read(mSettings.mPackages);
+ mPackageUsage.read(packageSettings);
mCompilerStats.read();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
@@ -3689,8 +3796,8 @@ public class PackageManagerService extends IPackageManager.Stub
// profile compilation (without waiting to collect a fresh set of profiles).
if (mIsUpgrade && !mOnlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
- for (int i = 0; i < mSettings.mPackages.size(); i++) {
- final PackageSetting ps = mSettings.mPackages.valueAt(i);
+ for (int i = 0; i < packageSettings.size(); i++) {
+ final PackageSetting ps = packageSettings.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
@@ -3706,9 +3813,9 @@ public class PackageManagerService extends IPackageManager.Stub
// their icons in launcher.
if (!mOnlyCore && mIsPreQUpgrade) {
Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
- int size = mSettings.mPackages.size();
+ int size = packageSettings.size();
for (int i = 0; i < size; i++) {
- final PackageSetting ps = mSettings.mPackages.valueAt(i);
+ final PackageSetting ps = packageSettings.valueAt(i);
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
@@ -3764,7 +3871,6 @@ public class PackageManagerService extends IPackageManager.Stub
UserHandle.USER_SYSTEM).getLongVersionCode());
// Initialize InstantAppRegistry's Instant App list for all users.
- final int[] userIds = UserManagerService.getInstance().getUserIds();
for (AndroidPackage pkg : mPackages.values()) {
if (pkg.isSystem()) {
continue;
@@ -3778,23 +3884,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- // Prepare a supplier of package parser for the staging manager to parse apex file
- // during the staging installation.
- final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2(
- mSeparateProcesses, mOnlyCore, mMetrics, null /* cacheDir */,
- mPackageParserCallback);
- mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
- final Pair<ComponentName, String> instantAppResolverComponent =
- getInstantAppResolverLPr();
+ mInstallerService = mInjector.getPackageInstallerService();
+ final ComponentName instantAppResolverComponent = getInstantAppResolverLPr();
if (instantAppResolverComponent != null) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
}
- mInstantAppResolverConnection = new InstantAppResolverConnection(
- mContext, instantAppResolverComponent.first,
- instantAppResolverComponent.second);
+ mInstantAppResolverConnection =
+ mInjector.getInstantAppResolverConnection(instantAppResolverComponent);
mInstantAppResolverSettingsComponent =
- getInstantAppResolverSettingsLPr(instantAppResolverComponent.first);
+ getInstantAppResolverSettingsLPr(instantAppResolverComponent);
} else {
mInstantAppResolverConnection = null;
mInstantAppResolverSettingsComponent = null;
@@ -3824,10 +3923,8 @@ public class PackageManagerService extends IPackageManager.Stub
} // synchronized (mInstallLock)
// CHECKSTYLE:ON IndentationCheck
- mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
-
- // Uncork cache invalidations and allow clients to cache package information.
- PackageManager.uncorkPackageInfoCache();
+ mModuleInfoProvider = mInjector.getModuleInfoProvider();
+ mInjector.getSystemWrapper().enablePackageCaches();
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
@@ -3875,7 +3972,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
// skip if the package has been disabled by the user
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
@@ -3900,7 +3997,7 @@ public class PackageManagerService extends IPackageManager.Stub
// disable any stub still left; these failed to install the full application
for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
final String pkgName = systemStubPackageNames.get(i);
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final PackageSetting ps = mSettings.getPackageLPr(pkgName);
ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
UserHandle.USER_SYSTEM, "android");
logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
@@ -3930,7 +4027,12 @@ public class PackageManagerService extends IPackageManager.Stub
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
}
- mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
+ final int[] userIds = mUserManager.getUserIds();
+ for (final int userId : userIds) {
+ mPermissionManager.onPackageInstalled(pkg,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+ userId);
+ }
writeSettingsLPrTEMP();
}
} catch (PackageManagerException e) {
@@ -3952,7 +4054,7 @@ public class PackageManagerService extends IPackageManager.Stub
} finally {
// Disable the package; the stub by itself is not runnable
synchronized (mLock) {
- final PackageSetting stubPs = mSettings.mPackages.get(
+ final PackageSetting stubPs = mSettings.getPackageLPr(
stubPkg.getPackageName());
if (stubPs != null) {
stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
@@ -4072,19 +4174,18 @@ public class PackageManagerService extends IPackageManager.Stub
setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
}
- private static @Nullable File preparePackageParserCache(Injector injector) {
+ private @Nullable File preparePackageParserCache(boolean forEngBuild) {
if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
return null;
}
// Disable package parsing on eng builds to allow for faster incremental development.
- if (Build.IS_ENG) {
+ if (forEngBuild) {
return null;
}
- if (injector.getSystemWrapper()
- .getPropertyBoolean("pm.boot.disable_package_cache", false)) {
+ if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
Slog.i(TAG, "Disabling package parser cache due to system property.");
return null;
}
@@ -4100,8 +4201,7 @@ public class PackageManagerService extends IPackageManager.Stub
// identify cached items. In particular, changing the value of certain
// feature flags should cause us to invalidate any caches.
final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
- : injector.getSystemWrapper().digestOfProperties(
- "ro.build.fingerprint");
+ : SystemProperties.digestOf("ro.build.fingerprint");
// Reconcile cache directories, keeping only what we'd actually use.
for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
@@ -4130,14 +4230,15 @@ public class PackageManagerService extends IPackageManager.Stub
// NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
// that starts with "eng." to signify that this is an engineering build and not
// destined for release.
- if (Build.IS_USERDEBUG && Build.VERSION.INCREMENTAL.startsWith("eng.")) {
+ if (mIsUserDebugBuild && mIncrementalVersion.startsWith("eng.")) {
Slog.w(TAG, "Wiping cache directory because the system partition changed.");
// Heuristic: If the /system directory has been modified recently due to an "adb sync"
// or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
// in general and should not be used for production changes. In this specific case,
// we know that they will work.
- File frameworkDir = new File(Environment.getRootDirectory(), "framework");
+ File frameworkDir =
+ new File(Environment.getRootDirectory(), "framework");
if (cacheDir.lastModified() < frameworkDir.lastModified()) {
FileUtils.deleteContents(cacheBaseDir);
cacheDir = FileUtils.createDir(cacheBaseDir, cacheName);
@@ -4163,7 +4264,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isDeviceUpgrading() {
// allow instant applications
// The system property allows testing ota flow when upgraded to the same image.
- return mIsUpgrade || mInjector.getSystemWrapper().getPropertyBoolean(
+ return mIsUpgrade || SystemProperties.getBoolean(
"persist.pm.mock-upgrade", false /* default */);
}
@@ -4297,15 +4398,15 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
synchronized (mLock) {
- final Pair<ComponentName, String> instantAppResolver = getInstantAppResolverLPr();
+ final ComponentName instantAppResolver = getInstantAppResolverLPr();
if (instantAppResolver == null) {
return null;
}
- return instantAppResolver.first;
+ return instantAppResolver;
}
}
- private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() {
+ private @Nullable ComponentName getInstantAppResolverLPr() {
final String[] packageArray =
mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
@@ -4320,8 +4421,7 @@ public class PackageManagerService extends IPackageManager.Stub
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
- String actionName = Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE;
- final Intent resolverIntent = new Intent(actionName);
+ final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
resolveFlags, UserHandle.USER_SYSTEM, callingUid, false /*includeInstantApps*/);
final int N = resolvers.size();
@@ -4353,7 +4453,7 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.v(TAG, "Ephemeral resolver found;"
+ " pkg: " + packageName + ", info:" + info);
}
- return new Pair<>(new ComponentName(packageName, info.serviceInfo.name), actionName);
+ return new ComponentName(packageName, info.serviceInfo.name);
}
if (DEBUG_INSTANT) {
Slog.v(TAG, "Ephemeral resolver NOT found");
@@ -4363,7 +4463,7 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mLock")
private @Nullable ActivityInfo getInstantAppInstallerLPr() {
- String[] orderedActions = Build.IS_ENG
+ String[] orderedActions = mIsEngBuild
? new String[]{
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST",
Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}
@@ -4374,7 +4474,7 @@ public class PackageManagerService extends IPackageManager.Stub
MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE
| Intent.FLAG_IGNORE_EPHEMERAL
- | (!Build.IS_ENG ? MATCH_SYSTEM_ONLY : 0);
+ | (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY);
final Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
@@ -4394,8 +4494,9 @@ public class PackageManagerService extends IPackageManager.Stub
Iterator<ResolveInfo> iter = matches.iterator();
while (iter.hasNext()) {
final ResolveInfo rInfo = iter.next();
- if (checkPermission(Manifest.permission.INSTALL_PACKAGES,
- rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || Build.IS_ENG) {
+ if (checkPermission(
+ Manifest.permission.INSTALL_PACKAGES,
+ rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || mIsEngBuild) {
continue;
}
iter.remove();
@@ -4623,7 +4724,7 @@ public class PackageManagerService extends IPackageManager.Stub
enforceCrossUserPermission(callingUid, userId, false, false, "checkPackageStartable");
final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
throw new SecurityException("Package " + packageName + " was not found!");
}
@@ -4739,7 +4840,7 @@ public class PackageManagerService extends IPackageManager.Stub
return generatePackageInfo(ps, flags, userId);
}
if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
@@ -4947,7 +5048,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
for (int i=names.length-1; i>=0; i--) {
- final PackageSetting ps = mSettings.mPackages.get(names[i]);
+ final PackageSetting ps = mSettings.getPackageLPr(names[i]);
boolean translateName = false;
if (ps != null && ps.realName != null) {
final boolean targetIsInstantApp = ps.getInstantApp(callingUserId);
@@ -4977,7 +5078,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String cur = mSettings.getRenamedPackageLPr(names[i]);
boolean translateName = false;
if (cur != null) {
- final PackageSetting ps = mSettings.mPackages.get(names[i]);
+ final PackageSetting ps = mSettings.getPackageLPr(names[i]);
final boolean targetIsInstantApp =
ps != null && ps.getInstantApp(callingUserId);
translateName = !targetIsInstantApp
@@ -5013,7 +5114,7 @@ public class PackageManagerService extends IPackageManager.Stub
return UserHandle.getUid(userId, p.getUid());
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return UserHandle.getUid(userId, ps.appId);
@@ -5045,7 +5146,7 @@ public class PackageManagerService extends IPackageManager.Stub
return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
@@ -5071,7 +5172,7 @@ public class PackageManagerService extends IPackageManager.Stub
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
@@ -5129,7 +5230,7 @@ public class PackageManagerService extends IPackageManager.Stub
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) return null;
if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
return null;
@@ -5598,7 +5699,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (a == null) {
return false;
}
- PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) {
return false;
}
@@ -5638,7 +5739,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
- PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_RECEIVER, userId)) {
@@ -5798,9 +5899,9 @@ public class PackageManagerService extends IPackageManager.Stub
private List<VersionedPackage> getPackagesUsingSharedLibraryLPr(
SharedLibraryInfo libInfo, int flags, int userId) {
List<VersionedPackage> versionedPackages = null;
- final int packageCount = mSettings.mPackages.size();
+ final int packageCount = mSettings.getPackagesLocked().size();
for (int i = 0; i < packageCount; i++) {
- PackageSetting ps = mSettings.mPackages.valueAt(i);
+ PackageSetting ps = mSettings.getPackagesLocked().valueAt(i);
if (ps == null) {
continue;
@@ -5859,7 +5960,7 @@ public class PackageManagerService extends IPackageManager.Stub
AndroidPackage pkg = mPackages.get(s.getPackageName());
if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) {
- PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_SERVICE, userId)) {
@@ -5893,7 +5994,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (mSettings.isEnabledAndMatchLPr(pkg, p, flags, userId)) {
- PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_PROVIDER, userId)) {
@@ -6030,7 +6131,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = changedPackages.get(i);
if (packageName != null) {
// Filter out the changes if the calling package should not be able to see it.
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
@@ -6051,7 +6152,7 @@ public class PackageManagerService extends IPackageManager.Stub
res.addAll(mAvailableFeatures.values());
}
final FeatureInfo fi = new FeatureInfo();
- fi.reqGlEsVersion = mInjector.getSystemWrapper().getPropertyInt("ro.opengles.version",
+ fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
FeatureInfo.GL_ES_VERSION_UNDEFINED);
res.add(fi);
@@ -6777,7 +6878,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int n = 0; n < count; n++) {
final ResolveInfo info = resolvedActivities.get(n);
final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
// only check domain verification status if the app is not a browser
if (!info.handleAllWebDataURI) {
@@ -6859,7 +6960,7 @@ public class PackageManagerService extends IPackageManager.Stub
// If we have an ephemeral app, use it
if (ri.activityInfo.applicationInfo.isInstantApp()) {
final String packageName = ri.activityInfo.packageName;
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int)(packedStatus >> 32);
if (status != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
@@ -7293,7 +7394,7 @@ public class PackageManagerService extends IPackageManager.Stub
private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId) {
- CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolvers(userId);
+ CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
if (resolver != null) {
return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
}
@@ -7541,7 +7642,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int i = instantApps.size() - 1; i >= 0; --i) {
final ResolveInfo info = instantApps.get(i);
final String packageName = info.activityInfo.packageName;
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps.getInstantApp(userId)) {
final long packedStatus = getDomainVerificationStatusLPr(ps, userId);
final int status = (int)(packedStatus >> 32);
@@ -7596,7 +7697,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (intent.isWebIntent() && auxiliaryResponse == null) {
return result;
}
- final PackageSetting ps = mSettings.mPackages.get(mInstantAppInstallerActivity.packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(mInstantAppInstallerActivity.packageName);
if (ps == null
|| !ps.readUserState(userId).isEnabled(mInstantAppInstallerActivity, 0)) {
return result;
@@ -7656,7 +7757,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
String packageName = riTargetUser.activityInfo.packageName;
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
continue;
}
@@ -7881,7 +7982,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int n=0; n<count; n++) {
ResolveInfo info = candidates.get(n);
String packageName = info.activityInfo.packageName;
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
// Add to the special match all list (Browser use case)
if (info.handleAllWebDataURI) {
@@ -7960,8 +8061,8 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
// Browser/generic handling case. If there's a default browser, go straight
// to that (but only if there is no other higher-priority match).
- final String defaultBrowserPackageName =
- mPermissionManager.getDefaultBrowser(userId);
+ final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(
+ userId);
int maxMatchPrio = 0;
ResolveInfo defaultBrowserMatch = null;
final int numCandidates = matchAllList.size();
@@ -8745,8 +8846,8 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
ArrayList<PackageInfo> list;
if (listUninstalled) {
- list = new ArrayList<>(mSettings.mPackages.size());
- for (PackageSetting ps : mSettings.mPackages.values()) {
+ list = new ArrayList<>(mSettings.getPackagesLocked().size());
+ for (PackageSetting ps : mSettings.getPackagesLocked().values()) {
if (listFactory) {
if (!ps.isSystem()) {
continue;
@@ -8858,7 +8959,7 @@ public class PackageManagerService extends IPackageManager.Stub
ArrayList<PackageInfo> list = new ArrayList<>();
boolean[] tmpBools = new boolean[permissions.length];
if (listUninstalled) {
- for (PackageSetting ps : mSettings.mPackages.values()) {
+ for (PackageSetting ps : mSettings.getPackagesLocked().values()) {
addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
userId);
}
@@ -8903,8 +9004,8 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
ArrayList<ApplicationInfo> list;
if (listUninstalled) {
- list = new ArrayList<>(mSettings.mPackages.size());
- for (PackageSetting ps : mSettings.mPackages.values()) {
+ list = new ArrayList<>(mSettings.getPackagesLocked().size());
+ for (PackageSetting ps : mSettings.getPackagesLocked().values()) {
ApplicationInfo ai;
int effectiveFlags = flags;
if (ps.isSystem()) {
@@ -8996,7 +9097,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
final boolean returnAllowed =
ps != null
&& (isCallerSameApp(packageName, callingUid)
@@ -9095,7 +9196,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (p.isPersistent()
&& (!mSafeMode || p.isSystem())
&& (matchesUnaware || matchesAware)) {
- PackageSetting ps = mSettings.mPackages.get(p.getPackageName());
+ PackageSetting ps = mSettings.getPackageLPr(p.getPackageName());
if (ps != null) {
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
ps.readUserState(userId), userId, ps);
@@ -9140,7 +9241,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
return null;
}
- final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName);
final ComponentName component =
new ComponentName(providerInfo.packageName, providerInfo.name);
if (shouldFilterApplicationLocked(ps, callingUid, component, TYPE_PROVIDER, userId)) {
@@ -9180,7 +9281,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
continue;
}
- final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName);
final ComponentName component =
new ComponentName(providerInfo.packageName, providerInfo.name);
if (shouldFilterApplicationLocked(
@@ -9209,7 +9310,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
String packageName = component.getPackageName();
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
AndroidPackage pkg = mPackages.get(packageName);
if (ps == null || pkg == null) return null;
if (shouldFilterApplicationLocked(
@@ -9441,8 +9542,7 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
- try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, mOnlyCore, mMetrics, null,
- mPackageParserCallback)) {
+ try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
@@ -9736,7 +9836,7 @@ public class PackageManagerService extends IPackageManager.Stub
pkgName, getSettingsVersionForPackage(parsedPackage)),
Collections.singletonMap(pkgName,
getSharedLibLatestVersionSetting(scanResult))),
- mSettings.mKeySetManagerService, mInjector);
+ mSettings.getKeySetManagerService(), mInjector);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mUserManager.getUserIds());
@@ -10047,7 +10147,7 @@ public class PackageManagerService extends IPackageManager.Stub
List<PackageSetting> pkgSettings;
synchronized (mLock) {
pkgSettings = PackageManagerServiceUtils.getPackagesForDexopt(
- mSettings.mPackages.values(), this);
+ mSettings.getPackagesLocked().values(), this);
}
List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
@@ -10192,7 +10292,7 @@ public class PackageManagerService extends IPackageManager.Stub
pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
}
- if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) {
+ if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
mArtManagerService.compileLayouts(pkg);
}
@@ -10405,7 +10505,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
- mPackageUsage.maybeWriteAsync(mSettings.mPackages);
+ mPackageUsage.maybeWriteAsync(mSettings.getPackagesLocked());
mCompilerStats.maybeWriteAsync();
}
final long callingId = Binder.clearCallingIdentity();
@@ -10630,13 +10730,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
public void shutdown() {
- mPackageUsage.writeNow(mSettings.mPackages);
mCompilerStats.writeNow();
mDexManager.writePackageDexUsageNow();
PackageWatchdog.getInstance(mContext).writeNow();
- // This is the last chance to write out pending restriction settings
synchronized (mLock) {
+ mPackageUsage.writeNow(mSettings.getPackagesLocked());
+
+ // This is the last chance to write out pending restriction settings
if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
for (int userId : mDirtyUsers) {
@@ -10764,7 +10865,7 @@ public class PackageManagerService extends IPackageManager.Stub
private void clearAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
final PackageSetting ps;
synchronized (mLock) {
- ps = mSettings.mPackages.get(pkg.getPackageName());
+ ps = mSettings.getPackageLPr(pkg.getPackageName());
}
for (int realUserId : resolveUserIds(userId)) {
final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
@@ -10788,7 +10889,7 @@ public class PackageManagerService extends IPackageManager.Stub
private void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
final PackageSetting ps;
synchronized (mLock) {
- ps = mSettings.mPackages.get(pkg.getPackageName());
+ ps = mSettings.getPackageLPr(pkg.getPackageName());
}
for (int realUserId : resolveUserIds(userId)) {
final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
@@ -11168,7 +11269,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
private int getVendorPartitionVersion() {
- final String version = mInjector.getSystemWrapper().getProperty("ro.vndk.version");
+ final String version = SystemProperties.get("ro.vndk.version");
if (!version.isEmpty()) {
try {
return Integer.parseInt(version);
@@ -11379,7 +11480,7 @@ public class PackageManagerService extends IPackageManager.Stub
// to allowlist their privileged permissions just like other
// priv-apps.
synchronized (mLock) {
- PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
+ PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
pkg.getSigningDetails().signatures)
!= PackageManager.SIGNATURE_MATCH)) {
@@ -11501,6 +11602,7 @@ public class PackageManagerService extends IPackageManager.Stub
parsedPackage.setVersionCode(mSdkVersion)
.setVersionCodeMajor(0);
}
+
final AndroidPackage oldPkg = request.oldPkg;
final @ParseFlags int parseFlags = request.parseFlags;
final @ScanFlags int scanFlags = request.scanFlags;
@@ -11540,7 +11642,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (reconciledPkg.installArgs != null) {
InstallSource installSource = reconciledPkg.installArgs.installSource;
if (installSource.initiatingPackageName != null) {
- final PackageSetting ips = mSettings.mPackages.get(
+ final PackageSetting ips = mSettings.getPackageLPr(
installSource.initiatingPackageName);
if (ips != null) {
installSource = installSource.setInitiatingPackageSignatures(
@@ -11570,7 +11672,7 @@ public class PackageManagerService extends IPackageManager.Stub
reconciledPkg.collectedSharedLibraryInfos, allUsers);
}
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
if (reconciledPkg.removeAppKeySetData) {
ksms.removeAppKeySetDataLPw(pkg.getPackageName());
}
@@ -11932,12 +12034,13 @@ public class PackageManagerService extends IPackageManager.Stub
final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride);
final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+ final File appLib32InstallDir = getAppLib32InstallDir();
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
if (needToDeriveAbi) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
- cpuAbiOverride);
+ cpuAbiOverride, appLib32InstallDir);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -11955,7 +12058,7 @@ public class PackageManagerService extends IPackageManager.Stub
abis.applyTo(pkgSetting);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, sAppLib32InstallDir);
+ isUpdatedSystemApp, appLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
} else {
@@ -11967,7 +12070,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, sAppLib32InstallDir);
+ isUpdatedSystemApp, appLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
if (DEBUG_ABI_SELECTION) {
@@ -11993,7 +12096,7 @@ public class PackageManagerService extends IPackageManager.Stub
// package path (after the rename away from the stage path).
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
- sAppLib32InstallDir);
+ appLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
@@ -12288,7 +12391,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Make sure we're not adding any bogus keyset info
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
ksms.assertScannedPackageValid(pkg);
synchronized (mLock) {
@@ -12506,7 +12609,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
// Exempt SharedUsers signed with the platform key.
- PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
+ PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
if (!comparePackageSignatures(platformPkgSetting,
pkg.getSigningDetails().signatures)) {
throw new PackageManagerException("Apps that share a user with a " +
@@ -12680,7 +12783,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
for (VersionedPackage dependentPackage : dependents) {
- final PackageSetting ps = mSettings.mPackages.get(
+ final PackageSetting ps = mSettings.getPackageLPr(
dependentPackage.getPackageName());
if (ps != null) {
ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
@@ -12839,7 +12942,6 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
// We don't expect installation to fail beyond this point
-
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
@@ -12849,7 +12951,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Add the package's KeySets to the global KeySetManagerService
- KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ KeySetManagerService ksms = mSettings.getKeySetManagerService();
ksms.addScannedPackageLPw(pkg);
mComponentResolver.addAllComponents(pkg, chatty);
@@ -13324,7 +13426,7 @@ public class PackageManagerService extends IPackageManager.Stub
packageName, extras, 0, null, null, userIds, instantUserIds,
mAppsFilter.getVisibilityAllowList(
getPackageSettingInternal(packageName, Process.SYSTEM_UID),
- userIds, mSettings.mPackages));
+ userIds, mSettings.getPackagesLocked()));
if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
mHandler.post(() -> {
for (int userId : userIds) {
@@ -13395,7 +13497,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean sendRemoved = false;
// writer
synchronized (mLock) {
- pkgSetting = mSettings.mPackages.get(packageName);
+ pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting == null) {
return false;
}
@@ -13461,7 +13563,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
synchronized (mLock) {
- final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting == null || !pkgSetting.isSystem()) {
return;
}
@@ -13488,7 +13590,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
synchronized (mLock) {
- final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
// The target app should always be in system
if (pkgSetting == null || !pkgSetting.isSystem()) {
return false;
@@ -13575,7 +13677,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
// writer
synchronized (mLock) {
- ps = mSettings.mPackages.get(packageName);
+ ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
return true;
}
@@ -13636,7 +13738,7 @@ public class PackageManagerService extends IPackageManager.Stub
// writer
synchronized (mLock) {
- pkgSetting = mSettings.mPackages.get(packageName);
+ pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
@@ -13671,14 +13773,16 @@ public class PackageManagerService extends IPackageManager.Stub
if (installed) {
if (pkgSetting.pkg != null) {
+ final PermissionManagerServiceInternal.PackageInstalledParams.Builder
+ permissionParamsBuilder =
+ new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
!= 0) {
- allowlistedRestrictedPermissions = pkgSetting.pkg.getRequestedPermissions();
- } else if (allowlistedRestrictedPermissions == null) {
- allowlistedRestrictedPermissions = Collections.emptyList();
+ permissionParamsBuilder.setAllowlistedRestrictedPermissions(
+ pkgSetting.pkg.getRequestedPermissions());
}
- mPermissionManager.onPackageInstalled(pkgSetting.pkg, Collections.emptyList(),
- allowlistedRestrictedPermissions, MODE_DEFAULT, userId);
+ mPermissionManager.onPackageInstalled(pkgSetting.pkg,
+ permissionParamsBuilder.build(), userId);
}
if (pkgSetting.pkg != null) {
@@ -13784,7 +13888,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = packageNames[i];
final PackageSetting pkgSetting;
synchronized (mLock) {
- pkgSetting = mSettings.mPackages.get(packageName);
+ pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting == null
|| shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
@@ -13882,7 +13986,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
final PackageSetting pkgSetting;
synchronized (mLock) {
- pkgSetting = mSettings.mPackages.get(packageName);
+ pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting == null
|| shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageName
@@ -13935,9 +14039,9 @@ public class PackageManagerService extends IPackageManager.Stub
private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
- throw new IllegalArgumentException("Unknown target package: " + packageName);
+ return null;
}
final PackageUserState pus = ps.readUserState(userId);
final Bundle allExtras = new Bundle();
@@ -13990,7 +14094,7 @@ public class PackageManagerService extends IPackageManager.Stub
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
false /* checkShell */, "isPackageSuspendedForUser for user " + userId);
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
}
@@ -14008,7 +14112,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
synchronized (mLock) {
- for (final PackageSetting ps : mSettings.mPackages.values()) {
+ for (final PackageSetting ps : mSettings.getPackagesLocked().values()) {
if (ps.isSuspendedBy(suspendingPackage, userId)) {
return true;
}
@@ -14034,7 +14138,7 @@ public class PackageManagerService extends IPackageManager.Stub
final IntArray unsuspendedUids = new IntArray();
synchronized (mLock) {
for (String packageName : packagesToChange) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && ps.getSuspended(userId)) {
ps.removeSuspension(suspendingPackagePredicate, userId);
if (!ps.getSuspended(userId)) {
@@ -14075,7 +14179,7 @@ public class PackageManagerService extends IPackageManager.Stub
final IntArray changedUids = new IntArray();
synchronized (mLock) {
for (String packageName : packagesToChange) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && ps.getDistractionFlags(userId) != 0) {
ps.setDistractionFlags(0, userId);
changedPackages.add(ps.name);
@@ -14131,7 +14235,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageNames[i]);
+ final PackageSetting ps = mSettings.getPackageLPr(packageNames[i]);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
unactionablePackages.add(packageNames[i]);
@@ -14155,8 +14259,8 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId);
final long callingId = Binder.clearCallingIdentity();
try {
- final String activeLauncherPackageName = mPermissionManager.getDefaultHome(userId);
- final String dialerPackageName = mPermissionManager.getDefaultDialer(userId);
+ final String activeLauncherPackageName = mDefaultAppProvider.getDefaultHome(userId);
+ final String dialerPackageName = mDefaultAppProvider.getDefaultDialer(userId);
for (int i = 0; i < packageNames.length; i++) {
canSuspend[i] = false;
final String packageName = packageNames[i];
@@ -14478,7 +14582,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Check if the developer wants to skip verification for ADB installs
if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
synchronized (mLock) {
- if (mSettings.mPackages.get(pkgInfoLite.packageName) == null) {
+ if (mSettings.getPackageLPr(pkgInfoLite.packageName) == null) {
// Always verify fresh install
return true;
}
@@ -14545,7 +14649,7 @@ public class PackageManagerService extends IPackageManager.Stub
return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null
|| shouldFilterApplicationLocked(
ps, callingUid, UserHandle.getUserId(callingUid))) {
@@ -14562,7 +14666,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean result = false;
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (shouldFilterApplicationLocked(
ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
return false;
@@ -14583,7 +14687,7 @@ public class PackageManagerService extends IPackageManager.Stub
return ParceledListSlice.emptyList();
}
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) {
return ParceledListSlice.emptyList();
}
@@ -14648,7 +14752,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// writer
synchronized (mLock) {
- PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
+ PackageSetting targetPackageSetting = mSettings.getPackageLPr(targetPackage);
if (targetPackageSetting == null
|| shouldFilterApplicationLocked(
targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) {
@@ -14657,7 +14761,7 @@ public class PackageManagerService extends IPackageManager.Stub
PackageSetting installerPackageSetting;
if (installerPackageName != null) {
- installerPackageSetting = mSettings.mPackages.get(installerPackageName);
+ installerPackageSetting = mSettings.getPackageLPr(installerPackageName);
if (installerPackageSetting == null) {
throw new IllegalArgumentException("Unknown installer package: "
+ installerPackageName);
@@ -14699,7 +14803,7 @@ public class PackageManagerService extends IPackageManager.Stub
String targetInstallerPackageName =
targetPackageSetting.installSource.installerPackageName;
PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null :
- mSettings.mPackages.get(targetInstallerPackageName);
+ mSettings.getPackageLPr(targetInstallerPackageName);
if (targetInstallerPkgSetting != null) {
if (compareSignatures(callerSignature,
@@ -14750,7 +14854,7 @@ public class PackageManagerService extends IPackageManager.Stub
mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
callerPackageName);
synchronized (mLock) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
@@ -15208,6 +15312,7 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean forceQueryableOverride;
final int mDataLoaderType;
final long requiredInstalledVersionCode;
+ final PackageLite mPackageLite;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
@@ -15229,11 +15334,13 @@ public class PackageManagerService extends IPackageManager.Stub
this.forceQueryableOverride = false;
this.mDataLoaderType = DataLoaderType.NONE;
this.requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
+ this.mPackageLite = null;
}
InstallParams(File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
- UserHandle user, SigningDetails signingDetails, int installerUid) {
+ UserHandle user, SigningDetails signingDetails, int installerUid,
+ PackageLite packageLite) {
super(user);
origin = OriginInfo.fromStagedFile(stagedDir);
move = null;
@@ -15252,6 +15359,7 @@ public class PackageManagerService extends IPackageManager.Stub
mDataLoaderType = (sessionParams.dataLoaderParams != null)
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
+ mPackageLite = packageLite;
}
@Override
@@ -15336,7 +15444,8 @@ public class PackageManagerService extends IPackageManager.Stub
try {
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
+ mPackageLite, origin.resolvedPath, installFlags,
+ packageAbiOverride);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to free cache", e);
}
@@ -15397,7 +15506,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
public void handleStartCopy() {
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
+ mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
// For staged session, there is a delay between its verification and install. Device
// state can change within this delay and hence we need to re-verify certain conditions.
@@ -15517,9 +15626,11 @@ public class PackageManagerService extends IPackageManager.Stub
private boolean mWaitForEnableRollbackToComplete;
private int mRet;
+ final PackageLite mPackageLite;
+
VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
PackageInstaller.SessionParams sessionParams, InstallSource installSource,
- int installerUid, SigningDetails signingDetails, int sessionId) {
+ int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite) {
super(user);
origin = OriginInfo.fromStagedFile(stagedDir);
this.observer = observer;
@@ -15537,6 +15648,7 @@ public class PackageManagerService extends IPackageManager.Stub
mDataLoaderType = (sessionParams.dataLoaderParams != null)
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
mSessionId = sessionId;
+ mPackageLite = lite;
}
@Override
@@ -15554,7 +15666,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- origin.resolvedPath, installFlags, packageAbiOverride);
+ mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);
if (mRet != INSTALL_SUCCEEDED) {
@@ -16480,12 +16592,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
synchronized (mLock) {
-// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
- mPermissionManager.updatePermissions(pkgName, pkg);
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final PackageSetting ps = mSettings.getPackageLPr(pkgName);
final int userId = installArgs.user.getIdentifier();
if (ps != null) {
if (pkg.isSystem()) {
@@ -16530,7 +16640,7 @@ public class PackageManagerService extends IPackageManager.Stub
// TODO(146804378): Support overlaying static shared libraries
continue;
}
- final PackageSetting libPs = mSettings.mPackages.get(
+ final PackageSetting libPs = mSettings.getPackageLPr(
sharedLib.getPackageName());
if (libPs == null) {
continue;
@@ -16943,8 +17053,7 @@ public class PackageManagerService extends IPackageManager.Stub
&& compareSignatures(sharedUserSignatures,
parsedPackage.getSigningDetails().signatures)
!= PackageManager.SIGNATURE_MATCH) {
- if (injector.getSystemWrapper()
- .getPropertyInt("ro.product.first_api_level", 0) <= 29) {
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
// Mismatched signatures is an error and silently skipping system
// packages will likely break the device in unforeseen ways.
// However, we allow the device to boot anyway because, prior to Q,
@@ -17134,7 +17243,7 @@ public class PackageManagerService extends IPackageManager.Stub
reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis();
res.removedInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList(
- reconciledPkg.pkgSetting, request.mAllUsers, mSettings.mPackages);
+ reconciledPkg.pkgSetting, request.mAllUsers, mSettings.getPackagesLocked());
if (reconciledPkg.prepareResult.system) {
// Remove existing system package
removePackageLI(oldPackage, true);
@@ -17158,7 +17267,7 @@ public class PackageManagerService extends IPackageManager.Stub
executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
true, request.mAllUsers, false, parsedPackage);
} catch (SystemDeleteException e) {
- if (Build.IS_ENG) {
+ if (mIsEngBuild) {
throw new RuntimeException("Unexpected failure", e);
// ignore; not possible for non-system app
}
@@ -17179,7 +17288,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Update the in-memory copy of the previous code paths.
- PackageSetting ps1 = mSettings.mPackages.get(
+ PackageSetting ps1 = mSettings.getPackageLPr(
reconciledPkg.prepareResult.existingPackage.getPackageName());
if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
== 0) {
@@ -17208,7 +17317,7 @@ public class PackageManagerService extends IPackageManager.Stub
AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers);
updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res);
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
@@ -17318,7 +17427,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
reconciledPackages = reconcilePackagesLocked(
- reconcileRequest, mSettings.mKeySetManagerService, mInjector);
+ reconcileRequest, mSettings.getKeySetManagerService(), mInjector);
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
request.installResult.setError("Reconciliation failed...", e);
@@ -17453,7 +17562,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (performDexopt) {
// Compile the layout resources.
- if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) {
+ if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
mViewCompiler.compileLayouts(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -17796,7 +17905,7 @@ public class PackageManagerService extends IPackageManager.Stub
final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
if (sourcePackageName.equals(parsedPackage.getPackageName())
&& (ksms.shouldCheckUpgradeKeySetLocked(
sourcePackageSetting, scanFlags))) {
@@ -17880,8 +17989,7 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
- try (PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null,
- mPackageParserCallback)) {
+ try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {
parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
} catch (PackageParserException e) {
@@ -17930,8 +18038,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
parsedPackage.setSigningDetails(args.signingDetails);
} else {
- parsedPackage.setSigningDetails(
- ParsingPackageUtils.getSigningDetails(parsedPackage, false /* skipVerify */));
+ parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails(
+ parsedPackage, false /* skipVerify */));
}
} catch (PackageParserException e) {
throw new PrepareFailure("Failed collect during installPackageLI", e);
@@ -17995,7 +18103,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- PackageSetting ps = mSettings.mPackages.get(pkgName);
+ PackageSetting ps = mSettings.getPackageLPr(pkgName);
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
@@ -18014,7 +18122,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Quick validity check that we're signed correctly if updating;
// we'll check this again later when scanning, but we want to
// bail early here before tripping over redefined permissions.
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
@@ -18195,7 +18303,7 @@ public class PackageManagerService extends IPackageManager.Stub
scanFlags |= SCAN_MOVE;
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(pkgName);
+ final PackageSetting ps = mSettings.getPackageLPr(pkgName);
if (ps == null) {
res.setError(INSTALL_FAILED_INTERNAL_ERROR,
"Missing settings for moved package " + pkgName);
@@ -18224,7 +18332,7 @@ public class PackageManagerService extends IPackageManager.Stub
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride);
+ abiOverride, mAppLib32InstallDir);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -18293,11 +18401,11 @@ public class PackageManagerService extends IPackageManager.Stub
"replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
}
- ps = mSettings.mPackages.get(pkgName11);
+ ps = mSettings.getPackageLPr(pkgName11);
disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
// verify signatures are valid
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
@@ -18500,7 +18608,7 @@ public class PackageManagerService extends IPackageManager.Stub
ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
if (legacyMode) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
+ final PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());
if (ps != null && ps.isPrivileged()) {
fsverityCandidates.put(pkg.getBaseApkPath(), null);
if (pkg.getSplitCodePaths() != null) {
@@ -18918,7 +19026,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
int returnCode;
- final PackageSetting ps = mSettings.mPackages.get(internalPackageName);
+ final PackageSetting ps = mSettings.getPackageLPr(internalPackageName);
boolean doDeletePackage = true;
if (ps != null) {
final boolean targetIsInstantApp =
@@ -19196,7 +19304,7 @@ public class PackageManagerService extends IPackageManager.Stub
int[] allUsers;
/** enabled state of the uninstalled application */
synchronized (mLock) {
- uninstalledPs = mSettings.mPackages.get(packageName);
+ uninstalledPs = mSettings.getPackageLPr(packageName);
if (uninstalledPs == null) {
Slog.w(TAG, "Not removing non-existent package " + packageName);
return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
@@ -19495,19 +19603,26 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL, true);
clearDefaultBrowserIfNeeded(packageName);
- mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
+ mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName);
mAppsFilter.removePackage(getPackageSetting(packageName));
removedAppId = mSettings.removePackageLPw(packageName);
if (outInfo != null) {
outInfo.removedAppId = removedAppId;
}
- final SharedUserSetting sus = deletedPs.getSharedUser();
- List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null;
- if (sharedUserPkgs == null) {
- sharedUserPkgs = Collections.emptyList();
+ if (!mSettings.isDisabledSystemPackageLPr(packageName)) {
+ // If we don't have a disabled system package to reinstall, the package is
+ // really gone and its permission state should be removed.
+ final SharedUserSetting sus = deletedPs.getSharedUser();
+ List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages()
+ : null;
+ if (sharedUserPkgs == null) {
+ sharedUserPkgs = Collections.emptyList();
+ }
+ for (final int userId : allUserHandles) {
+ mPermissionManager.onPackageUninstalled(packageName, deletedPs.appId,
+ deletedPs.pkg, sharedUserPkgs, userId);
+ }
}
- mPermissionManager.onPackageStateRemoved(packageName, deletedPs.appId,
- deletedPs.pkg, sharedUserPkgs);
clearPackagePreferredActivitiesLPw(
deletedPs.name, changedUsers, UserHandle.USER_ALL);
}
@@ -19638,7 +19753,7 @@ public class PackageManagerService extends IPackageManager.Stub
// We've re-installed the stub; make sure it's disabled here. If package was
// originally enabled, we'll install the compressed version of the application
// and re-enable it afterward.
- final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
+ final PackageSetting stubPs = mSettings.getPackageLPr(deletedPkg.getPackageName());
if (stubPs != null) {
int userId = action.user == null
? UserHandle.USER_ALL : action.user.getIdentifier();
@@ -19694,11 +19809,7 @@ public class PackageManagerService extends IPackageManager.Stub
// writer
synchronized (mLock) {
- PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
-
- // The update permissions method below will take care of removing obsolete permissions
- // and granting install permissions.
- mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
+ PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());
final boolean applyUserRestrictions = origUserHandles != null;
if (applyUserRestrictions) {
@@ -19718,8 +19829,6 @@ public class PackageManagerService extends IPackageManager.Stub
if (installed) {
ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
}
-
- mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
// Regardless of writeSettings we need to ensure that this restriction
// state propagation is persisted
@@ -19728,6 +19837,17 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.writeKernelMappingLPr(ps);
}
}
+
+ for (final int userId : allUserHandles) {
+ // The method below will take care of removing obsolete permissions and granting
+ // install permissions.
+ mPermissionManager.onPackageInstalled(pkg,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT, userId);
+ if (applyUserRestrictions) {
+ mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+ }
+ }
+
// can downgrade to reader here
if (writeSettings) {
writeSettingsLPrTEMP();
@@ -19782,7 +19902,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean getBlockUninstallForUser(String packageName, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null || shouldFilterApplicationLocked(ps, Binder.getCallingUid(), userId)) {
return false;
}
@@ -19794,7 +19914,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root");
synchronized (mLock) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
Log.w(TAG, "Package doesn't exist: " + packageName);
return false;
@@ -19862,7 +19982,7 @@ public class PackageManagerService extends IPackageManager.Stub
ParsedPackage replacingPackage) {
final DeletePackageAction action;
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
action = mayDeletePackageLocked(outInfo, ps, disabledPs, flags, user);
}
@@ -20026,6 +20146,11 @@ public class PackageManagerService extends IPackageManager.Stub
destroyAppProfilesLIF(pkg);
+ final SharedUserSetting sus = ps.getSharedUser();
+ List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null;
+ if (sharedUserPkgs == null) {
+ sharedUserPkgs = Collections.emptyList();
+ }
final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds()
: new int[] {userId};
for (int nextUserId : userIds) {
@@ -20040,7 +20165,8 @@ public class PackageManagerService extends IPackageManager.Stub
clearDefaultBrowserIfNeededForUser(ps.name, nextUserId);
removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId);
clearPackagePreferredActivities(ps.name, nextUserId);
- mPermissionManager.resetRuntimePermissions(pkg, nextUserId);
+ mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs,
+ nextUserId);
}
if (outInfo != null) {
@@ -20146,7 +20272,7 @@ public class PackageManagerService extends IPackageManager.Stub
PackageSetting ps;
synchronized (mLock) {
pkg = mPackages.get(packageName);
- ps = mSettings.mPackages.get(packageName);
+ ps = mSettings.getPackageLPr(packageName);
if (pkg == null) {
if (ps != null) {
pkg = ps.pkg;
@@ -20283,7 +20409,7 @@ public class PackageManagerService extends IPackageManager.Stub
private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
final PackageSetting ps;
synchronized (mLock) {
- ps = mSettings.mPackages.get(packageName);
+ ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
Slog.w(TAG, "Failed to find settings for " + packageName);
return false;
@@ -20590,10 +20716,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void clearDefaultBrowserIfNeededForUser(String packageName, int userId) {
- final String defaultBrowserPackageName = mPermissionManager.getDefaultBrowser(userId);
+ final String defaultBrowserPackageName = mDefaultAppProvider.getDefaultBrowser(userId);
if (!TextUtils.isEmpty(defaultBrowserPackageName)) {
if (packageName.equals(defaultBrowserPackageName)) {
- mPermissionManager.setDefaultBrowser(null, true, true, userId);
+ mDefaultAppProvider.setDefaultBrowser(null, true, userId);
}
}
}
@@ -20608,14 +20734,13 @@ public class PackageManagerService extends IPackageManager.Stub
// If this browser is restored from user's backup, do not clear
// default-browser state for this user
if (installReason != PackageManager.INSTALL_REASON_DEVICE_RESTORE) {
- mPermissionManager.setDefaultBrowser(null, true, true, userId);
+ mDefaultAppProvider.setDefaultBrowser(null, true, userId);
}
}
// We may also need to apply pending (restored) runtime permission grants
// within these users.
- mPermissionManager.restoreDelayedRuntimePermissions(packageName,
- UserHandle.of(userId));
+ mPermissionManager.restoreDelayedRuntimePermissions(packageName, userId);
// Persistent preferred activity might have came into effect due to this
// install.
@@ -20647,7 +20772,7 @@ public class PackageManagerService extends IPackageManager.Stub
// significant refactoring to keep all default apps in the package
// manager (cleaner but more work) or have the services provide
// callbacks to the package manager to request a default app reset.
- mPermissionManager.setDefaultBrowser(null, true, true, userId);
+ mDefaultAppProvider.setDefaultBrowser(null, true, userId);
resetNetworkPolicies(userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
@@ -20881,8 +21006,7 @@ public class PackageManagerService extends IPackageManager.Stub
defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
}
if (defaultBrowser != null) {
- mPermissionManager
- .setDefaultBrowser(defaultBrowser, false, false, userId1);
+ mDefaultAppProvider.setDefaultBrowser(defaultBrowser, false, userId1);
}
});
} catch (Exception e) {
@@ -21122,7 +21246,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
allHomeCandidates.addAll(resolveInfos);
- final String packageName = mPermissionManager.getDefaultHome(userId);
+ final String packageName = mDefaultAppProvider.getDefaultHome(userId);
if (packageName == null) {
return null;
}
@@ -21176,7 +21300,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageName = preferredResolveInfo != null
&& preferredResolveInfo.activityInfo != null
? preferredResolveInfo.activityInfo.packageName : null;
- final String currentPackageName = mPermissionManager.getDefaultHome(userId);
+ final String currentPackageName = mDefaultAppProvider.getDefaultHome(userId);
if (TextUtils.equals(currentPackageName, packageName)) {
return false;
}
@@ -21191,12 +21315,12 @@ public class PackageManagerService extends IPackageManager.Stub
// Keep the default home package in RoleManager.
return false;
}
- mPermissionManager.setDefaultHome(packageName, userId, (successful) -> {
- if (successful) {
- postPreferredActivityChangedBroadcast(userId);
- }
- });
- return true;
+ return mDefaultAppProvider.setDefaultHome(packageName, userId, mContext.getMainExecutor(),
+ successful -> {
+ if (successful) {
+ postPreferredActivityChangedBroadcast(userId);
+ }
+ });
}
@Override
@@ -21459,7 +21583,7 @@ public class PackageManagerService extends IPackageManager.Stub
public void setUpdateAvailable(String packageName, boolean updateAvailable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
synchronized (mLock) {
- final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting != null) {
pkgSetting.setUpdateAvailable(updateAvailable);
}
@@ -21584,7 +21708,7 @@ public class PackageManagerService extends IPackageManager.Stub
// reader
synchronized (mLock) {
- pkgSetting = mSettings.mPackages.get(packageName);
+ pkgSetting = mSettings.getPackageLPr(packageName);
if (pkgSetting == null) {
if (!isCallerInstantApp) {
if (className == null) {
@@ -21848,7 +21972,7 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
broadcastAllowList = isInstantApp ? null : mAppsFilter.getVisibilityAllowList(setting,
- userIds, mSettings.mPackages);
+ userIds, mSettings.getPackagesLocked());
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
userIds, instantUserIds, broadcastAllowList);
@@ -21868,7 +21992,7 @@ public class PackageManagerService extends IPackageManager.Stub
true /* checkShell */, "stop package");
// writer
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (!shouldFilterApplicationLocked(ps, callingUid, userId)
&& mSettings.setPackageStoppedStateLPw(this, packageName, stopped,
allowedByPermission, callingUid, userId)) {
@@ -21887,7 +22011,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
String installerPackageName = installSource.installerPackageName;
if (installerPackageName != null) {
- final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
+ final PackageSetting ps = mSettings.getPackageLPr(installerPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid,
UserHandle.getUserId(callingUid))) {
installerPackageName = null;
@@ -21916,7 +22040,7 @@ public class PackageManagerService extends IPackageManager.Stub
installerPackageName = installSource.installerPackageName;
if (installerPackageName != null) {
- final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
+ final PackageSetting ps = mSettings.getPackageLPr(installerPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
installerPackageName = null;
}
@@ -21941,7 +22065,7 @@ public class PackageManagerService extends IPackageManager.Stub
initiatingPackageName = installerPackageName;
} else {
initiatingPackageName = installSource.initiatingPackageName;
- final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
+ final PackageSetting ps = mSettings.getPackageLPr(initiatingPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
initiatingPackageName = null;
}
@@ -21950,7 +22074,7 @@ public class PackageManagerService extends IPackageManager.Stub
originatingPackageName = installSource.originatingPackageName;
if (originatingPackageName != null) {
- final PackageSetting ps = mSettings.mPackages.get(originatingPackageName);
+ final PackageSetting ps = mSettings.getPackageLPr(originatingPackageName);
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
originatingPackageName = null;
}
@@ -21983,7 +22107,7 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mLock")
@Nullable
private InstallSource getInstallSourceLocked(String packageName, int callingUid) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
// Installer info for Apex is not stored in PackageManager
if (ps == null && mApexManager.isApexPackage(packageName)) {
@@ -22142,6 +22266,24 @@ public class PackageManagerService extends IPackageManager.Stub
mPermissionManager.systemReady();
+ int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
+ for (int userId : UserManagerService.getInstance().getUserIds()) {
+ if (mPmInternal.isPermissionUpgradeNeeded(userId)) {
+ grantPermissionsUserIds = ArrayUtils.appendInt(
+ grantPermissionsUserIds, userId);
+ }
+ }
+ // If we upgraded grant all default permissions before kicking off.
+ for (int userId : grantPermissionsUserIds) {
+ mLegacyPermissionManager.grantDefaultPermissions(userId);
+ }
+ if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
+ // If we did not grant default permissions, we preload from this the
+ // default permission exceptions lazily to ensure we don't hit the
+ // disk on a new user creation.
+ mLegacyPermissionManager.scheduleReadDefaultPermissionExceptions();
+ }
+
if (mInstantAppResolverConnection != null) {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
@@ -22646,13 +22788,14 @@ public class PackageManagerService extends IPackageManager.Stub
&& dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
&& packageName == null) {
pw.println();
- int count = mSettings.mPackages.size();
+ int count = mSettings.getPackagesLocked().size();
if (count == 0) {
pw.println("No applications!");
pw.println();
} else {
final String prefix = " ";
- Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values();
+ Collection<PackageSetting> allPackageSettings =
+ mSettings.getPackagesLocked().values();
if (allPackageSettings.size() == 0) {
pw.println("No domain preferred apps!");
pw.println();
@@ -22709,7 +22852,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
- mSettings.mKeySetManagerService.dumpLPr(pw, packageName, dumpState);
+ mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
}
if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
@@ -22840,7 +22983,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (ArrayUtils.isEmpty(apkList)) {
return;
}
- String sku = mInjector.getSystemWrapper().getProperty("ro.boot.hardware.sku");
+ String sku = SystemProperties.get("ro.boot.hardware.sku");
if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
return;
}
@@ -22936,7 +23079,7 @@ public class PackageManagerService extends IPackageManager.Stub
ipw.increaseIndent();
Collection<PackageSetting> pkgSettings;
if (packageName != null) {
- PackageSetting targetPkgSetting = mSettings.mPackages.get(packageName);
+ PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName);
if (targetPkgSetting != null) {
pkgSettings = Collections.singletonList(targetPkgSetting);
} else {
@@ -22944,7 +23087,7 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
} else {
- pkgSettings = mSettings.mPackages.values();
+ pkgSettings = mSettings.getPackagesLocked().values();
}
for (PackageSetting pkgSetting : pkgSettings) {
@@ -23245,7 +23388,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Normalize package name to handle renamed packages
packageName = normalizePackageNameLPr(packageName);
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
throw new PackageManagerException("Package " + packageName + " is unknown");
} else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
@@ -23262,9 +23405,9 @@ public class PackageManagerService extends IPackageManager.Stub
private List<String> collectAbsoluteCodePaths() {
synchronized (mLock) {
List<String> codePaths = new ArrayList<>();
- final int packageCount = mSettings.mPackages.size();
+ final int packageCount = mSettings.getPackagesLocked().size();
for (int i = 0; i < packageCount; i++) {
- final PackageSetting ps = mSettings.mPackages.valueAt(i);
+ final PackageSetting ps = mSettings.getPackagesLocked().valueAt(i);
codePaths.add(ps.getPath().getAbsolutePath());
}
return codePaths;
@@ -23466,7 +23609,7 @@ public class PackageManagerService extends IPackageManager.Stub
private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
final PackageSetting ps;
synchronized (mLock) {
- ps = mSettings.mPackages.get(pkg.getPackageName());
+ ps = mSettings.getPackageLPr(pkg.getPackageName());
mSettings.writeKernelMappingLPr(ps);
}
@@ -23543,7 +23686,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting ps;
synchronized (mLock) {
- ps = mSettings.mPackages.get(pkg.getPackageName());
+ ps = mSettings.getPackageLPr(pkg.getPackageName());
}
final String volumeUuid = pkg.getVolumeUuid();
final String packageName = pkg.getPackageName();
@@ -23562,7 +23705,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
- if ((e != null) && pkg.isSystem()) {
+ if (e != null) {
logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
+ ", but trying to recover: " + e);
destroyAppDataLeafLIF(pkg, userId, flags);
@@ -23738,7 +23881,7 @@ public class PackageManagerService extends IPackageManager.Stub
mPackageName = packageName;
mWeFroze = mFrozenPackages.add(mPackageName);
- final PackageSetting ps = mSettings.mPackages.get(mPackageName);
+ final PackageSetting ps = mSettings.getPackageLPr(mPackageName);
if (ps != null) {
killApplication(ps.name, ps.appId, userId, killReason);
}
@@ -23820,7 +23963,7 @@ public class PackageManagerService extends IPackageManager.Stub
// reader
synchronized (mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (pkg == null
|| ps == null
|| shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) {
@@ -24151,9 +24294,9 @@ public class PackageManagerService extends IPackageManager.Stub
private void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) {
final boolean DEBUG_CLEAN_APKS = false;
int [] users = userManager.getUserIds();
- final int numPackages = mSettings.mPackages.size();
+ final int numPackages = mSettings.getPackagesLocked().size();
for (int index = 0; index < numPackages; index++) {
- final PackageSetting ps = mSettings.mPackages.valueAt(index);
+ final PackageSetting ps = mSettings.getPackagesLocked().valueAt(index);
if (ps.pkg == null) {
continue;
}
@@ -24222,14 +24365,9 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.d(TAG, "onNewUserCreated(id=" + userId
+ ", convertedFromPreCreated=" + convertedFromPreCreated + ")");
}
- if (!convertedFromPreCreated) {
- mPermissionManager.onNewUserCreated(userId);
- return;
- }
- if (!readPermissionStateForUser(userId)) {
- // Could not read the existing permissions, re-grant them.
- Slog.i(TAG, "re-granting permissions for pre-created user " + userId);
- mPermissionManager.onNewUserCreated(userId);
+ if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) {
+ mPermissionManager.onUserCreated(userId);
+ mLegacyPermissionManager.grantDefaultPermissions(userId);
}
}
@@ -24322,7 +24460,7 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.w(TAG, "KeySet requested for filtered package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias));
}
}
@@ -24351,7 +24489,7 @@ public class PackageManagerService extends IPackageManager.Stub
&& Process.SYSTEM_UID != callingUid) {
throw new SecurityException("May not access signing KeySet of other apps.");
}
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName));
}
}
@@ -24375,7 +24513,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
IBinder ksh = ks.getToken();
if (ksh instanceof KeySetHandle) {
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh);
}
return false;
@@ -24401,7 +24539,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
IBinder ksh = ks.getToken();
if (ksh instanceof KeySetHandle) {
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ final KeySetManagerService ksms = mSettings.getKeySetManagerService();
return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh);
}
return false;
@@ -24410,7 +24548,7 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mLock")
private void deletePackageIfUnusedLPr(final String packageName) {
- PackageSetting ps = mSettings.mPackages.get(packageName);
+ PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
return;
}
@@ -24746,7 +24884,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isPlatformSigned(String packageName) {
- PackageSetting packageSetting = mSettings.mPackages.get(packageName);
+ PackageSetting packageSetting = mSettings.getPackageLPr(packageName);
if (packageSetting == null) {
return false;
}
@@ -24946,7 +25084,7 @@ public class PackageManagerService extends IPackageManager.Stub
private String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
switch (knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
- return new String[]{mPermissionManager.getDefaultBrowser(userId)};
+ return new String[] { mDefaultAppProvider.getDefaultBrowser(userId) };
case PackageManagerInternal.PACKAGE_INSTALLER:
return filterOnlySystemPackages(mRequiredInstallerPackage);
case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
@@ -25035,7 +25173,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public long getCeDataInode(String packageName, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
return ps.getCeDataInode(userId);
}
@@ -25046,7 +25184,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
final Bundle allExtras = new Bundle();
if (ps != null) {
final PackageUserState pus = ps.readUserState(userId);
@@ -25068,7 +25206,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isPackageSuspended(String packageName, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
return (ps != null) ? ps.getSuspended(userId) : false;
}
}
@@ -25113,7 +25251,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public String getSuspendingPackage(String suspendedPackage, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
+ final PackageSetting ps = mSettings.getPackageLPr(suspendedPackage);
if (ps != null) {
final PackageUserState pus = ps.readUserState(userId);
if (pus.suspended) {
@@ -25135,7 +25273,7 @@ public class PackageManagerService extends IPackageManager.Stub
public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
String suspendingPackage, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
+ final PackageSetting ps = mSettings.getPackageLPr(suspendedPackage);
if (ps != null) {
final PackageUserState pus = ps.readUserState(userId);
if (pus.suspended) {
@@ -25151,7 +25289,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int getDistractingPackageRestrictions(String packageName, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE;
}
}
@@ -25247,7 +25385,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isPackageEphemeral(int userId, String packageName) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
return ps != null ? ps.getInstantApp(userId) : false;
}
}
@@ -25438,7 +25576,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
for (VersionedPackage dependent : dependents) {
- final PackageSetting ps = mSettings.mPackages.get(
+ final PackageSetting ps = mSettings.getPackageLPr(
dependent.getPackageName());
if (ps == null) {
continue;
@@ -25449,7 +25587,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
+ final PackageSetting ps = mSettings.getPackageLPr(targetPackageName);
ps.setOverlayPaths(overlayPaths, userId);
outUpdatedPackageNames.add(targetPackageName);
@@ -25519,7 +25657,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean canAccessComponent(int callingUid, ComponentName component, int userId) {
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ final PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
return ps != null && !PackageManagerService.this.shouldFilterApplicationLocked(
ps, callingUid, component, TYPE_UNKNOWN, userId);
}
@@ -25585,8 +25723,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) {
synchronized (mLock) {
- for (int index = 0; index < mSettings.mPackages.size(); index++) {
- actionLocked.accept(mSettings.mPackages.valueAt(index));
+ for (int index = 0; index < mSettings.getPackagesLocked().size(); index++) {
+ actionLocked.accept(mSettings.getPackagesLocked().valueAt(index));
}
}
}
@@ -25762,7 +25900,7 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
final PackageSetting installerPackageSetting =
- mSettings.mPackages.get(packageSetting.installSource.installerPackageName);
+ mSettings.getPackageLPr(packageSetting.installSource.installerPackageName);
return installerPackageSetting != null
&& UserHandle.isSameApp(installerPackageSetting.appId, callingUid);
}
@@ -25776,18 +25914,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public void setReadExternalStorageEnforced(boolean enforced) {
- synchronized (mLock) {
- if (mSettings.mReadExternalStorageEnforced != null
- && mSettings.mReadExternalStorageEnforced == enforced) {
- return;
- }
- mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE;
- writeSettingsLPrTEMP();
- }
- }
-
- @Override
public void setIntegrityVerificationResult(int verificationId, int verificationResult) {
final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE);
msg.arg1 = verificationId;
@@ -25863,7 +25989,7 @@ public class PackageManagerService extends IPackageManager.Stub
PackageManagerInternal.InstalledLoadingProgressCallback callback) {
final PackageSetting ps;
synchronized (mLock) {
- ps = mSettings.mPackages.get(packageName);
+ ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
Slog.w(TAG, "Failed unregistering loading progress callback. Package "
+ packageName + " is not installed");
@@ -25926,7 +26052,7 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mLock")
@NonNull
private String[] getSharedUserPackagesForPackageLocked(String packageName, int userId) {
- final PackageSetting packageSetting = mSettings.mPackages.get(packageName);
+ final PackageSetting packageSetting = mSettings.getPackageLPr(packageName);
if (packageSetting == null || !packageSetting.isSharedUser()) {
return EmptyArray.STRING;
}
@@ -26003,7 +26129,7 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
packageName = resolveInternalPackageNameInternalLocked(
packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
- return mSettings.mPackages.get(packageName);
+ return mSettings.getPackageLPr(packageName);
}
}
@@ -26032,7 +26158,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
boolean isHistoricalPackageUsageAvailable() {
- return mPackageUsage.isHistoricalPackageUsageAvailable();
+ synchronized (mLock) {
+ return mPackageUsage.isHistoricalPackageUsageAvailable();
+ }
}
/**
@@ -26092,7 +26220,7 @@ public class PackageManagerService extends IPackageManager.Stub
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
false /* checkShell */, "get install reason");
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
+ final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return PackageManager.INSTALL_REASON_UNKNOWN;
}
@@ -26265,7 +26393,7 @@ public class PackageManagerService extends IPackageManager.Stub
long currentTimeInMillis = System.currentTimeMillis();
synchronized (mLock) {
for (AndroidPackage pkg : mPackages.values()) {
- PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
+ PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());
if (ps == null) {
continue;
}
@@ -26377,7 +26505,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
- boolean changed = mSettings.mPackages.get(packageName)
+ boolean changed = mSettings.getPackageLPr(packageName)
.setMimeGroup(mimeGroup, mimeTypes);
if (changed) {
@@ -26387,7 +26515,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public List<String> getMimeGroup(String packageName, String mimeGroup) {
- return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup);
+ return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup);
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9f1d6566e279..c77eda161c67 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -819,8 +819,8 @@ public class PackageManagerServiceUtils {
/**
* Parse given package and return minimal details.
*/
- public static PackageInfoLite getMinimalPackageInfo(Context context, String packagePath,
- int flags, String abiOverride) {
+ public static PackageInfoLite getMinimalPackageInfo(Context context,
+ PackageParser.PackageLite pkg, String packagePath, int flags, String abiOverride) {
final PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
@@ -829,14 +829,10 @@ public class PackageManagerServiceUtils {
}
final File packageFile = new File(packagePath);
- final PackageParser.PackageLite pkg;
final long sizeBytes;
try {
- pkg = PackageParser.parsePackageLite(packageFile, 0);
sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride);
- } catch (PackageParserException | IOException e) {
- Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
-
+ } catch (IOException e) {
if (!packageFile.exists()) {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
} else {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 9aa1a621a760..318b2293a13a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -146,7 +146,7 @@ class PackageManagerShellCommand extends ShellCommand {
final IPackageManager mInterface;
final IPermissionManager mPermissionManager;
- final Context mShellPackageContext;
+ final Context mContext;
final private WeakHashMap<String, Resources> mResourceCache =
new WeakHashMap<String, Resources>();
int mTargetUser;
@@ -158,12 +158,7 @@ class PackageManagerShellCommand extends ShellCommand {
PackageManagerService service, IPermissionManager permissionManager, Context context) {
mInterface = service;
mPermissionManager = permissionManager;
- try {
- mShellPackageContext = context.createPackageContext("com.android.shell", 0);
- } catch (NameNotFoundException e) {
- // should not happen
- throw new RuntimeException(e);
- }
+ mContext = context;
}
@Override
@@ -486,8 +481,17 @@ class PackageManagerShellCommand extends ShellCommand {
return 1;
}
+ final Context shellPackageContext;
+ try {
+ shellPackageContext = mContext.createPackageContextAsUser(
+ "com.android.shell", 0, Binder.getCallingUserHandle());
+ } catch (NameNotFoundException e) {
+ // should not happen
+ throw new RuntimeException(e);
+ }
+
final LocalIntentReceiver receiver = new LocalIntentReceiver();
- RollbackManager rm = mShellPackageContext.getSystemService(RollbackManager.class);
+ RollbackManager rm = shellPackageContext.getSystemService(RollbackManager.class);
RollbackInfo rollback = null;
for (RollbackInfo r : rm.getAvailableRollbacks()) {
for (PackageRollbackInfo info : r.getPackages()) {
@@ -549,7 +553,8 @@ class PackageManagerShellCommand extends ShellCommand {
+ apkLiteResult.getErrorMessage(),
apkLiteResult.getException());
}
- PackageLite pkgLite = new PackageLite(null, apkLiteResult.getResult(), null, null,
+ final ApkLite apkLite = apkLiteResult.getResult();
+ PackageLite pkgLite = new PackageLite(null, apkLite.codePath, apkLite, null, null,
null, null, null, null);
sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
@@ -2319,7 +2324,7 @@ class PackageManagerShellCommand extends ShellCommand {
getErrPrintWriter().println("Error: no enforcement specified");
return 1;
}
- mPermissionManager.setPermissionEnforced(permission, Boolean.parseBoolean(enforcedRaw));
+ // Permissions are always enforced now.
return 0;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f47b4b46fdd4..2d5034e624cd 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -362,8 +362,6 @@ public final class Settings {
}
}
- Boolean mReadExternalStorageEnforced;
-
/** Device identity for the purpose of package verification. */
private VerifierDeviceIdentity mVerifierDeviceIdentity;
@@ -475,6 +473,14 @@ public final class Settings {
return mPackages.get(pkgName);
}
+ ArrayMap<String, PackageSetting> getPackagesLocked() {
+ return mPackages;
+ }
+
+ KeySetManagerService getKeySetManagerService() {
+ return mKeySetManagerService;
+ }
+
String getRenamedPackageLPr(String pkgName) {
return mRenamedPackages.get(pkgName);
}
@@ -1969,28 +1975,28 @@ public final class Settings {
serializer.attributeLong(null, ATTR_CE_DATA_INODE, ustate.ceDataInode);
}
if (!ustate.installed) {
- serializer.attribute(null, ATTR_INSTALLED, "false");
+ serializer.attributeBoolean(null, ATTR_INSTALLED, false);
}
if (ustate.stopped) {
- serializer.attribute(null, ATTR_STOPPED, "true");
+ serializer.attributeBoolean(null, ATTR_STOPPED, true);
}
if (ustate.notLaunched) {
- serializer.attribute(null, ATTR_NOT_LAUNCHED, "true");
+ serializer.attributeBoolean(null, ATTR_NOT_LAUNCHED, true);
}
if (ustate.hidden) {
- serializer.attribute(null, ATTR_HIDDEN, "true");
+ serializer.attributeBoolean(null, ATTR_HIDDEN, true);
}
if (ustate.distractionFlags != 0) {
serializer.attributeInt(null, ATTR_DISTRACTION_FLAGS, ustate.distractionFlags);
}
if (ustate.suspended) {
- serializer.attribute(null, ATTR_SUSPENDED, "true");
+ serializer.attributeBoolean(null, ATTR_SUSPENDED, true);
}
if (ustate.instantApp) {
- serializer.attribute(null, ATTR_INSTANT_APP, "true");
+ serializer.attributeBoolean(null, ATTR_INSTANT_APP, true);
}
if (ustate.virtualPreload) {
- serializer.attribute(null, ATTR_VIRTUAL_PRELOAD, "true");
+ serializer.attributeBoolean(null, ATTR_VIRTUAL_PRELOAD, true);
}
if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
serializer.attributeInt(null, ATTR_ENABLED, ustate.enabled);
@@ -2334,13 +2340,6 @@ public final class Settings {
serializer.endTag(null, "verifier");
}
- if (mReadExternalStorageEnforced != null) {
- serializer.startTag(null, TAG_READ_EXTERNAL_STORAGE);
- serializer.attribute(
- null, ATTR_ENFORCEMENT, mReadExternalStorageEnforced ? "1" : "0");
- serializer.endTag(null, TAG_READ_EXTERNAL_STORAGE);
- }
-
serializer.startTag(null, "permission-trees");
mPermissions.writePermissionTrees(serializer);
serializer.endTag(null, "permission-trees");
@@ -2731,7 +2730,7 @@ public final class Settings {
serializer.attributeInt(null, "sharedUserId", pkg.appId);
}
if (pkg.uidError) {
- serializer.attribute(null, "uidError", "true");
+ serializer.attributeBoolean(null, "uidError", true);
}
InstallSource installSource = pkg.installSource;
if (installSource.installerPackageName != null) {
@@ -2742,13 +2741,13 @@ public final class Settings {
installSource.installerAttributionTag);
}
if (installSource.isOrphaned) {
- serializer.attribute(null, "isOrphaned", "true");
+ serializer.attributeBoolean(null, "isOrphaned", true);
}
if (installSource.initiatingPackageName != null) {
serializer.attribute(null, "installInitiator", installSource.initiatingPackageName);
}
if (installSource.isInitiatingPackageUninstalled) {
- serializer.attribute(null, "installInitiatorUninstalled", "true");
+ serializer.attributeBoolean(null, "installInitiatorUninstalled", true);
}
if (installSource.originatingPackageName != null) {
serializer.attribute(null, "installOriginator", installSource.originatingPackageName);
@@ -2760,16 +2759,16 @@ public final class Settings {
serializer.attributeInt(null, "categoryHint", pkg.categoryHint);
}
if (pkg.updateAvailable) {
- serializer.attribute(null, "updateAvailable", "true");
+ serializer.attributeBoolean(null, "updateAvailable", true);
}
if (pkg.forceQueryableOverride) {
- serializer.attribute(null, "forceQueryable", "true");
+ serializer.attributeBoolean(null, "forceQueryable", true);
}
if (pkg.isPackageStartable()) {
- serializer.attribute(null, "isStartable", "true");
+ serializer.attributeBoolean(null, "isStartable", true);
}
if (pkg.isPackageLoading()) {
- serializer.attribute(null, "isLoading", "true");
+ serializer.attributeBoolean(null, "isLoading", true);
}
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
@@ -2951,9 +2950,7 @@ public final class Settings {
+ e.getMessage());
}
} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {
- final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT);
- mReadExternalStorageEnforced =
- "1".equals(enforcement) ? Boolean.TRUE : Boolean.FALSE;
+ // No longer used.
} else if (tagName.equals("keyset-settings")) {
mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);
} else if (TAG_VERSION.equals(tagName)) {
@@ -3500,14 +3497,14 @@ public final class Settings {
String systemStr = null;
String installerPackageName = null;
String installerAttributionTag = null;
- String isOrphaned = null;
+ boolean isOrphaned = false;
String installOriginatingPackageName = null;
String installInitiatingPackageName = null;
- String installInitiatorUninstalled = null;
+ boolean installInitiatorUninstalled = false;
String volumeUuid = null;
- String updateAvailable = null;
+ boolean updateAvailable = false;
int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
- String uidError = null;
+ boolean uidError = false;
int pkgFlags = 0;
int pkgPrivateFlags = 0;
long timeStamp = 0;
@@ -3515,14 +3512,14 @@ public final class Settings {
long lastUpdateTime = 0;
PackageSetting packageSetting = null;
long versionCode = 0;
- String installedForceQueryable = null;
- String isStartable = null;
- String isLoading = null;
+ boolean installedForceQueryable = false;
+ boolean isStartable = false;
+ boolean isLoading = false;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
userId = parser.getAttributeInt(null, "userId", 0);
- uidError = parser.getAttributeValue(null, "uidError");
+ uidError = parser.getAttributeBoolean(null, "uidError", false);
sharedUserId = parser.getAttributeInt(null, "sharedUserId", 0);
codePathStr = parser.getAttributeValue(null, "codePath");
@@ -3532,10 +3529,10 @@ public final class Settings {
primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi");
secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi");
cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride");
- updateAvailable = parser.getAttributeValue(null, "updateAvailable");
- installedForceQueryable = parser.getAttributeValue(null, "forceQueryable");
- isStartable = parser.getAttributeValue(null, "isStartable");
- isLoading = parser.getAttributeValue(null, "isLoading");
+ updateAvailable = parser.getAttributeBoolean(null, "updateAvailable", false);
+ installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false);
+ isStartable = parser.getAttributeBoolean(null, "isStartable", false);
+ isLoading = parser.getAttributeBoolean(null, "isLoading", false);
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
primaryCpuAbiString = legacyCpuAbiString;
@@ -3544,11 +3541,11 @@ public final class Settings {
versionCode = parser.getAttributeLong(null, "version", 0);
installerPackageName = parser.getAttributeValue(null, "installer");
installerAttributionTag = parser.getAttributeValue(null, "installerAttributionTag");
- isOrphaned = parser.getAttributeValue(null, "isOrphaned");
+ isOrphaned = parser.getAttributeBoolean(null, "isOrphaned", false);
installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator");
installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator");
- installInitiatorUninstalled = parser.getAttributeValue(null,
- "installInitiatorUninstalled");
+ installInitiatorUninstalled = parser.getAttributeBoolean(null,
+ "installInitiatorUninstalled", false);
volumeUuid = parser.getAttributeValue(null, "volumeUuid");
categoryHint = parser.getAttributeInt(null, "categoryHint",
ApplicationInfo.CATEGORY_UNDEFINED);
@@ -3670,21 +3667,20 @@ public final class Settings {
+ userId + " at " + parser.getPositionDescription());
}
if (packageSetting != null) {
- packageSetting.uidError = "true".equals(uidError);
+ packageSetting.uidError = uidError;
InstallSource installSource = InstallSource.create(
installInitiatingPackageName, installOriginatingPackageName,
- installerPackageName, installerAttributionTag, "true".equals(isOrphaned),
- "true".equals(installInitiatorUninstalled));
+ installerPackageName, installerAttributionTag, isOrphaned,
+ installInitiatorUninstalled);
packageSetting.installSource = installSource;
packageSetting.volumeUuid = volumeUuid;
packageSetting.categoryHint = categoryHint;
packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
packageSetting.primaryCpuAbiString = primaryCpuAbiString;
packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
- packageSetting.updateAvailable = "true".equals(updateAvailable);
- packageSetting.forceQueryableOverride = "true".equals(installedForceQueryable);
- packageSetting.incrementalStates = new IncrementalStates("true".equals(isStartable),
- "true".equals(isLoading));
+ packageSetting.updateAvailable = updateAvailable;
+ packageSetting.forceQueryableOverride = installedForceQueryable;
+ packageSetting.incrementalStates = new IncrementalStates(isStartable, isLoading);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -3914,7 +3910,7 @@ public final class Settings {
{
name = parser.getAttributeValue(null, ATTR_NAME);
int userId = parser.getAttributeInt(null, "userId", 0);
- if ("true".equals(parser.getAttributeValue(null, "system"))) {
+ if (parser.getAttributeBoolean(null, "system", false)) {
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
}
if (name == null) {
@@ -4893,8 +4889,7 @@ public final class Settings {
DumpState dumpState) {
LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames,
mPermissionDataProvider.getLegacyPermissions(),
- mPermissionDataProvider.getAllAppOpPermissionPackages(),
- (mReadExternalStorageEnforced == Boolean.TRUE), dumpState);
+ mPermissionDataProvider.getAllAppOpPermissionPackages(), true, dumpState);
}
void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
@@ -5554,7 +5549,8 @@ public final class Settings {
return mPreferredActivities.get(userId);
}
- CrossProfileIntentResolver getCrossProfileIntentResolvers(int userId) {
+ @Nullable
+ CrossProfileIntentResolver getCrossProfileIntentResolver(int userId) {
return mCrossProfileIntentResolvers.get(userId);
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 7d48d0a6e266..e913829ea9e2 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -30,6 +30,14 @@
]
},
{
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm."
+ }
+ ]
+ },
+ {
"name": "CtsContentTestCases",
"options": [
{
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index cc814bcc7760..62ac57062ac6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2918,16 +2918,16 @@ public class UserManagerService extends IUserManager.Stub {
serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
}
if (userInfo.partial) {
- serializer.attribute(null, ATTR_PARTIAL, "true");
+ serializer.attributeBoolean(null, ATTR_PARTIAL, true);
}
if (userInfo.preCreated) {
- serializer.attribute(null, ATTR_PRE_CREATED, "true");
+ serializer.attributeBoolean(null, ATTR_PRE_CREATED, true);
}
if (userInfo.convertedFromPreCreated) {
- serializer.attribute(null, ATTR_CONVERTED_FROM_PRE_CREATED, "true");
+ serializer.attributeBoolean(null, ATTR_CONVERTED_FROM_PRE_CREATED, true);
}
if (userInfo.guestToRemove) {
- serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
+ serializer.attributeBoolean(null, ATTR_GUEST_TO_REMOVE, true);
}
if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
serializer.attributeInt(null, ATTR_PROFILE_GROUP_ID, userInfo.profileGroupId);
@@ -3121,22 +3121,10 @@ public class UserManagerService extends IUserManager.Stub {
profileBadge = parser.getAttributeInt(null, ATTR_PROFILE_BADGE, 0);
restrictedProfileParentId = parser.getAttributeInt(null,
ATTR_RESTRICTED_PROFILE_PARENT_ID, UserInfo.NO_PROFILE_GROUP_ID);
- String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
- if ("true".equals(valueString)) {
- partial = true;
- }
- valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED);
- if ("true".equals(valueString)) {
- preCreated = true;
- }
- valueString = parser.getAttributeValue(null, ATTR_CONVERTED_FROM_PRE_CREATED);
- if ("true".equals(valueString)) {
- converted = true;
- }
- valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
- if ("true".equals(valueString)) {
- guestToRemove = true;
- }
+ partial = parser.getAttributeBoolean(null, ATTR_PARTIAL, false);
+ preCreated = parser.getAttributeBoolean(null, ATTR_PRE_CREATED, false);
+ converted = parser.getAttributeBoolean(null, ATTR_CONVERTED_FROM_PRE_CREATED, false);
+ guestToRemove = parser.getAttributeBoolean(null, ATTR_GUEST_TO_REMOVE, false);
seedAccountName = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_NAME);
seedAccountType = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_TYPE);
@@ -4257,10 +4245,9 @@ public class UserManagerService extends IUserManager.Stub {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
String key = parser.getAttributeValue(null, ATTR_KEY);
String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
- String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
- if (multiple != null) {
+ int count = parser.getAttributeInt(null, ATTR_MULTIPLE, -1);
+ if (count != -1) {
values.clear();
- int count = Integer.parseInt(multiple);
while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG
&& parser.getName().equals(TAG_VALUE)) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 0ac3030ba5dc..51dff4063850 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -344,7 +344,7 @@ public class UserRestrictionsUtils {
}
if (USER_RESTRICTIONS.contains(key)) {
if (restrictions.getBoolean(key)) {
- serializer.attribute(null, key, "true");
+ serializer.attributeBoolean(null, key, true);
}
continue;
}
@@ -360,9 +360,9 @@ public class UserRestrictionsUtils {
public static void readRestrictions(TypedXmlPullParser parser, Bundle restrictions) {
restrictions.clear();
for (String key : USER_RESTRICTIONS) {
- final String value = parser.getAttributeValue(null, key);
- if (value != null) {
- restrictions.putBoolean(key, Boolean.parseBoolean(value));
+ final boolean value = parser.getAttributeBoolean(null, key, false);
+ if (value) {
+ restrictions.putBoolean(key, true);
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 165647205a98..44a2187aed8e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -41,8 +41,9 @@ import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
import android.os.Message;
+import android.os.Process;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.permission.PermissionManager;
@@ -67,8 +68,9 @@ import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
-import com.android.server.pm.permission.PermissionManagerServiceInternal.PackagesProvider;
-import com.android.server.pm.permission.PermissionManagerServiceInternal.SyncAdapterPackagesProvider;
+import com.android.server.ServiceThread;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal.PackagesProvider;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal.SyncAdapterPackagesProvider;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -96,7 +98,7 @@ import java.util.Set;
* to have an interface defined in the package manager but have the impl next to other
* policy stuff like PhoneWindowManager
*/
-public final class DefaultPermissionGrantPolicy {
+final class DefaultPermissionGrantPolicy {
private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
private static final boolean DEBUG = false;
@@ -291,9 +293,12 @@ public final class DefaultPermissionGrantPolicy {
}
};
- DefaultPermissionGrantPolicy(Context context, Looper looper) {
+ DefaultPermissionGrantPolicy(@NonNull Context context) {
mContext = context;
- mHandler = new Handler(looper) {
+ HandlerThread handlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
@@ -998,12 +1003,6 @@ public final class DefaultPermissionGrantPolicy {
}
}
- public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
- Log.i(TAG, "Granting permissions to default browser for user:" + userId);
- grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
- FOREGROUND_LOCATION_PERMISSIONS);
- }
-
private String getDefaultSystemHandlerActivityPackage(PackageManagerWrapper pm,
String intentAction, int userId) {
return getDefaultSystemHandlerActivityPackage(pm, new Intent(intentAction), userId);
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermission.java b/services/core/java/com/android/server/pm/permission/LegacyPermission.java
index 6a4eb63c8d7e..ca3a2e2e2da7 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermission.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermission.java
@@ -167,16 +167,12 @@ public final class LegacyPermission {
private static int readInt(@NonNull TypedXmlPullParser parser, @Nullable String namespace,
@NonNull String name, int defaultValue) {
- final String value = parser.getAttributeValue(namespace, name);
- if (value == null) {
- return defaultValue;
- }
try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
+ return parser.getAttributeInt(namespace, name);
+ } catch (Exception ignored) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: attribute " + name
- + " has bad integer value " + value + " at "
+ + " has bad integer value at "
+ parser.getPositionDescription());
return defaultValue;
}
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java
new file mode 100644
index 000000000000..a098484b803b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+
+/**
+ * The internal interface for {@link LegacyPermissionManagerService}.
+ */
+public interface LegacyPermissionManagerInternal {
+ /**
+ * Sets the dialer application packages provider.
+ * @param provider The provider.
+ */
+ void setDialerAppPackagesProvider(PackagesProvider provider);
+
+ /**
+ * Set the location extra packages provider.
+ * @param provider The packages provider.
+ */
+ void setLocationExtraPackagesProvider(PackagesProvider provider);
+
+ /**
+ * Sets the location provider packages provider.
+ * @param provider The packages provider.
+ */
+ void setLocationPackagesProvider(PackagesProvider provider);
+
+ /**
+ * Sets the SIM call manager packages provider.
+ * @param provider The provider.
+ */
+ void setSimCallManagerPackagesProvider(PackagesProvider provider);
+
+ /**
+ * Sets the SMS application packages provider.
+ * @param provider The provider.
+ */
+ void setSmsAppPackagesProvider(PackagesProvider provider);
+
+ /**
+ * Sets the sync adapter packages provider.
+ * @param provider The provider.
+ */
+ void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider);
+
+ /**
+ * Sets the Use Open Wifi packages provider.
+ * @param provider The packages provider.
+ */
+ void setUseOpenWifiAppPackagesProvider(PackagesProvider provider);
+
+ /**
+ * Sets the voice interaction packages provider.
+ * @param provider The packages provider.
+ */
+ void setVoiceInteractionPackagesProvider(PackagesProvider provider);
+
+ /**
+ * Requests granting of the default permissions to the current default Use Open Wifi app.
+ * @param packageName The default use open wifi package name.
+ * @param userId The user for which to grant the permissions.
+ */
+ void grantDefaultPermissionsToDefaultSimCallManager(@NonNull String packageName,
+ @UserIdInt int userId);
+
+ /**
+ * Requests granting of the default permissions to the current default Use Open Wifi app.
+ * @param packageName The default use open wifi package name.
+ * @param userId The user for which to grant the permissions.
+ */
+ void grantDefaultPermissionsToDefaultUseOpenWifiApp(@NonNull String packageName,
+ @UserIdInt int userId);
+
+ /**
+ * Grant the default permissions for a user.
+ *
+ * @param userId the user ID
+ */
+ void grantDefaultPermissions(@UserIdInt int userId);
+
+ /**
+ * Schedule reading the default permission exceptions file.
+ */
+ void scheduleReadDefaultPermissionExceptions();
+
+ // TODO(zhanghai): The following methods should be moved to a new AIDL to support
+ // the legacy PermissionManager directly in a later CL.
+
+ /**
+ * Grant default permissions to currently active LUI app
+ * @param packageName The package name for the LUI app
+ * @param userId The user ID
+ */
+ void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId);
+
+ /**
+ * Revoke default permissions to currently active LUI app
+ * @param packageNames The package names for the LUI apps
+ * @param userId The user ID
+ */
+ void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId);
+
+ /**
+ * Grant default permissions to currently active Ims services
+ * @param packageNames The package names for the Ims services
+ * @param userId The user ID
+ */
+ void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId);
+
+ /**
+ * Grant default permissions to currently enabled telephony data services
+ * @param packageNames The package name for the services
+ * @param userId The user ID
+ */
+ void grantDefaultPermissionsToEnabledTelephonyDataServices(String[] packageNames, int userId);
+
+ /**
+ * Revoke default permissions to currently active telephony data services
+ * @param packageNames The package name for the services
+ * @param userId The IDhandle
+ */
+ void revokeDefaultPermissionsFromDisabledTelephonyDataServices(String[] packageNames,
+ int userId);
+
+ /**
+ * Grant default permissions to currently enabled carrier apps
+ * @param packageNames Package names of the apps to be granted permissions
+ * @param userId The user ID
+ */
+ void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId);
+
+ /**
+ * Provider for package names.
+ */
+ interface PackagesProvider {
+ /**
+ * Gets the packages for a given user.
+ * @param userId The user id.
+ * @return The package names.
+ */
+ String[] getPackages(int userId);
+ }
+
+ /**
+ * Provider for package names.
+ */
+ interface SyncAdapterPackagesProvider {
+ /**
+ * Gets the sync adapter packages for given authority and user.
+ * @param authority The authority.
+ * @param userId The user id.
+ * @return The package names.
+ */
+ String[] getPackages(String authority, int userId);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
new file mode 100644
index 000000000000..0c0a8dfeaaec
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Binder;
+
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerServiceUtils;
+
+/**
+ * Legacy permission manager service.
+ */
+public class LegacyPermissionManagerService {
+ @NonNull
+ private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
+
+ /**
+ * Get or create an instance of this class for use by other components.
+ * <p>
+ * This method is not thread-safe.
+ *
+ * @param context the {@link Context}
+ * @return the internal instance
+ */
+ @NonNull
+ public static LegacyPermissionManagerInternal create(@NonNull Context context) {
+ LegacyPermissionManagerInternal legacyPermissionManagerInternal = LocalServices.getService(
+ LegacyPermissionManagerInternal.class);
+ if (legacyPermissionManagerInternal == null) {
+ new LegacyPermissionManagerService(context);
+ legacyPermissionManagerInternal = LocalServices.getService(
+ LegacyPermissionManagerInternal.class);
+ }
+ return legacyPermissionManagerInternal;
+ }
+
+ private LegacyPermissionManagerService(@NonNull Context context) {
+ mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(context);
+ LocalServices.addService(LegacyPermissionManagerInternal.class, new Internal());
+ }
+
+ private class Internal implements LegacyPermissionManagerInternal {
+ @Override
+ public void setDialerAppPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider);
+ }
+
+ @Override
+ public void setLocationExtraPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider);
+ }
+
+ @Override
+ public void setLocationPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider);
+ }
+
+ @Override
+ public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider);
+ }
+
+ @Override
+ public void setSmsAppPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider);
+ }
+
+ @Override
+ public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider);
+ }
+
+ @Override
+ public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider);
+ }
+
+ @Override
+ public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
+ mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider);
+ }
+
+ @Override
+ public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
+ mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager(
+ packageName, userId);
+ }
+
+ @Override
+ public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
+ mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp(
+ packageName, userId);
+ }
+
+ @Override
+ public void grantDefaultPermissions(int userId) {
+ mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
+ }
+
+ @Override
+ public void scheduleReadDefaultPermissionExceptions() {
+ mDefaultPermissionGrantPolicy.scheduleReadDefaultPermissionExceptions();
+ }
+
+ // TODO(zhanghai): The following methods should be moved to a new AIDL to support
+ // the legacy PermissionManager directly in a later CL.
+
+ @Override
+ public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
+ "grantDefaultPermissionsToActiveLuiApp", callingUid);
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToActiveLuiApp(packageName, userId));
+ }
+
+ @Override
+ public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
+ "revokeDefaultPermissionsFromLuiApps", callingUid);
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .revokeDefaultPermissionsFromLuiApps(packageNames, userId));
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
+ "grantDefaultPermissionsToEnabledImsServices", callingUid);
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToEnabledImsServices(packageNames, userId));
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledTelephonyDataServices(
+ String[] packageNames, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
+ "grantDefaultPermissionsToEnabledTelephonyDataServices", callingUid);
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId));
+ }
+
+ @Override
+ public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+ String[] packageNames, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
+ "revokeDefaultPermissionsFromDisabledTelephonyDataServices", callingUid);
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames,
+ userId));
+ }
+
+ @Override
+ public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
+ "grantPermissionsToEnabledCarrierApps", callingUid);
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId));
+ }
+ }
+}
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 52bb3d772387..791efd022fe9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -114,8 +114,6 @@ import android.permission.IPermissionManager;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
-import android.permission.PermissionManagerInternal.CheckPermissionDelegate;
-import android.permission.PermissionManagerInternal.OnRuntimePermissionStateChangedListener;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -138,6 +136,7 @@ import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -145,16 +144,12 @@ import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
import com.android.server.pm.ApexManager;
-import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultBrowserProvider;
-import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider;
-import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider;
-import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
@@ -177,7 +172,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
+import java.util.function.BiFunction;
/**
* Manages all permissions and handles permissions related tasks.
@@ -243,9 +238,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private final SparseArray<OneTimePermissionUserManager> mOneTimePermissionUserManagers =
new SparseArray<>();
- /** Default permission policy to provide proper behaviour out-of-the-box */
- private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
-
/** App ops manager */
private final AppOpsManager mAppOpsManager;
@@ -305,15 +297,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@NonNull
private final OnPermissionChangeListeners mOnPermissionChangeListeners;
- @GuardedBy("mLock")
- private DefaultBrowserProvider mDefaultBrowserProvider;
-
- @GuardedBy("mLock")
- private DefaultDialerProvider mDefaultDialerProvider;
-
- @GuardedBy("mLock")
- private DefaultHomeProvider mDefaultHomeProvider;
-
// TODO: Take a look at the methods defined in the callback.
// The callback was initially created to support the split between permission
// manager and the package manager. However, it's started to be used for other
@@ -403,8 +386,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mHandler = new Handler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler);
- mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
- context, mHandlerThread.getLooper());
SystemConfig systemConfig = SystemConfig.getInstance();
mSystemPermissions = systemConfig.getSystemPermissions();
mGlobalGids = systemConfig.getGlobalGids();
@@ -2019,145 +2000,41 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public String getDefaultBrowser(int userId) {
- final int callingUid = Binder.getCallingUid();
- if (UserHandle.getUserId(callingUid) != userId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- }
- if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
- return null;
- }
- DefaultBrowserProvider provider;
- synchronized (mLock) {
- provider = mDefaultBrowserProvider;
- }
- return provider != null ? provider.getDefaultBrowser(userId) : null;
- }
-
- @Override
- public boolean setDefaultBrowser(String packageName, int userId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- }
- return setDefaultBrowserInternal(packageName, false, true, userId);
- }
-
- private boolean setDefaultBrowserInternal(String packageName, boolean async,
- boolean doGrant, int userId) {
- if (userId == UserHandle.USER_ALL) {
- return false;
- }
- DefaultBrowserProvider provider;
- synchronized (mLock) {
- provider = mDefaultBrowserProvider;
- }
- if (provider == null) {
- return false;
- }
- if (async) {
- provider.setDefaultBrowserAsync(packageName, userId);
- } else {
- if (!provider.setDefaultBrowser(packageName, userId)) {
- return false;
- }
- }
- if (doGrant && packageName != null) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
- userId);
- }
- return true;
- }
-
- @Override
public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
- final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils
- .enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps", callingUid);
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId));
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
+ .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
}
@Override
public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
- final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
- "grantDefaultPermissionsToEnabledImsServices", callingUid);
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToEnabledImsServices(packageNames, userId));
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
+ .grantDefaultPermissionsToEnabledImsServices(packageNames, userId);
}
@Override
public void grantDefaultPermissionsToEnabledTelephonyDataServices(
String[] packageNames, int userId) {
- final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
- "grantDefaultPermissionsToEnabledTelephonyDataServices", callingUid);
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId));
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
+ .grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId);
}
@Override
public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
String[] packageNames, int userId) {
- final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
- "revokeDefaultPermissionsFromDisabledTelephonyDataServices", callingUid);
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames, userId));
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
+ .revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames, userId);
}
@Override
public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
- final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils
- .enforceSystemOrPhoneCaller("grantDefaultPermissionsToActiveLuiApp", callingUid);
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToActiveLuiApp(packageName, userId));
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
+ .grantDefaultPermissionsToActiveLuiApp(packageName, userId);
}
@Override
public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
- final int callingUid = Binder.getCallingUid();
- PackageManagerServiceUtils
- .enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromLuiApps", callingUid);
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .revokeDefaultPermissionsFromLuiApps(packageNames, userId));
- }
-
- @Override
- public void setPermissionEnforced(String permName, boolean enforced) {
- // TODO: Now that we no longer change GID for storage, this should to away.
- mContext.enforceCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
- "setPermissionEnforced");
- if (READ_EXTERNAL_STORAGE.equals(permName)) {
- mPackageManagerInt.setReadExternalStorageEnforced(enforced);
- // kill any non-foreground processes so we restart them and
- // grant/revoke the GID.
- final IActivityManager am = ActivityManager.getService();
- if (am != null) {
- final long token = Binder.clearCallingIdentity();
- try {
- am.killProcessesBelowForeground("setPermissionEnforcement");
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- } else {
- throw new IllegalArgumentException("No selective enforcement for " + permName);
- }
- }
-
- /** @deprecated */
- @Override
- @Deprecated
- public boolean isPermissionEnforced(String permName) {
- // allow instant applications
- return true;
+ LocalServices.getService(LegacyPermissionManagerInternal.class)
+ .revokeDefaultPermissionsFromLuiApps(packageNames, userId);
}
/**
@@ -2264,19 +2141,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* <p>Can not be called on main thread.
*
- * @param user The user the data should be extracted for
+ * @param userId The user ID the data should be extracted for
*
* @return The state as a xml file
*/
- private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
+ @Nullable
+ private byte[] backupRuntimePermissions(@UserIdInt int userId) {
CompletableFuture<byte[]> backup = new CompletableFuture<>();
- mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(),
- backup::complete);
+ mPermissionControllerManager.getRuntimePermissionBackup(UserHandle.of(userId),
+ mContext.getMainExecutor(), backup::complete);
try {
return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
- Slog.e(TAG, "Cannot create permission backup for " + user, e);
+ Slog.e(TAG, "Cannot create permission backup for user " + userId, e);
return null;
}
}
@@ -2288,13 +2166,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* applied via {@link #restoreDelayedRuntimePermissions}.
*
* @param backup The state as an xml file
- * @param user The user the data should be restored for
+ * @param userId The user ID the data should be restored for
*/
- private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
+ private void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) {
synchronized (mLock) {
- mHasNoDelayedPermBackup.delete(user.getIdentifier());
+ mHasNoDelayedPermBackup.delete(userId);
}
- mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup, user);
+ mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup,
+ UserHandle.of(userId));
}
/**
@@ -2303,24 +2182,24 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* <p>Can not be called on main thread.
*
* @param packageName The package that is newly installed
- * @param user The user the package is installed for
+ * @param userId The user ID the package is installed for
*
* @see #restoreRuntimePermissions
*/
private void restoreDelayedRuntimePermissions(@NonNull String packageName,
- @NonNull UserHandle user) {
+ @UserIdInt int userId) {
synchronized (mLock) {
- if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) {
+ if (mHasNoDelayedPermBackup.get(userId, false)) {
return;
}
}
- mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName, user,
- mContext.getMainExecutor(), (hasMoreBackup) -> {
+ mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName,
+ UserHandle.of(userId), mContext.getMainExecutor(), (hasMoreBackup) -> {
if (hasMoreBackup) {
return;
}
synchronized (mLock) {
- mHasNoDelayedPermBackup.put(user.getIdentifier(), true);
+ mHasNoDelayedPermBackup.put(userId, true);
}
});
}
@@ -2361,6 +2240,32 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ private void startShellPermissionIdentityDelegationInternal(int uid,
+ @NonNull String packageName, @Nullable List<String> permissionNames) {
+ synchronized (mLock) {
+ final CheckPermissionDelegate oldDelegate = mCheckPermissionDelegate;
+ if (oldDelegate != null && oldDelegate.getDelegatedUid() != uid) {
+ throw new SecurityException(
+ "Shell can delegate permissions only to one UID at a time");
+ }
+ final ShellDelegate delegate = new ShellDelegate(uid, packageName, permissionNames);
+ setCheckPermissionDelegateLocked(delegate);
+ }
+ }
+
+ private void stopShellPermissionIdentityDelegationInternal() {
+ synchronized (mLock) {
+ setCheckPermissionDelegateLocked(null);
+ }
+ }
+
+ private void setCheckPermissionDelegateLocked(@Nullable CheckPermissionDelegate delegate) {
+ if (delegate != null || mCheckPermissionDelegate != null) {
+ PackageManager.invalidatePackageInfoCache();
+ }
+ mCheckPermissionDelegate = delegate;
+ }
+
/**
* If the app is updated, and has scoped storage permissions, then it is possible that the
* app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
@@ -4139,17 +4044,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* @param packageName The package that is updated
* @param pkg The package that is updated, or {@code null} if package is deleted
- * @param allPackages All currently known packages
- * @param callback Callback to call after permission changes
*/
- private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg,
- @NonNull PermissionCallback callback) {
+ private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
// If the package is being deleted, update the permissions of all the apps
final int flags =
(pkg == null ? UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG
: UPDATE_PERMISSIONS_REPLACE_PKG);
updatePermissions(
- packageName, pkg, getVolumeUuidForPackage(pkg), flags, callback);
+ packageName, pkg, getVolumeUuidForPackage(pkg), flags, mDefaultPermissionCallback);
}
/**
@@ -4625,24 +4527,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
-
- int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
- for (int userId : UserManagerService.getInstance().getUserIds()) {
- if (mPackageManagerInt.isPermissionUpgradeNeeded(userId)) {
- grantPermissionsUserIds = ArrayUtils.appendInt(
- grantPermissionsUserIds, userId);
- }
- }
- // If we upgraded grant all default permissions before kicking off.
- for (int userId : grantPermissionsUserIds) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
- }
- if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
- // If we did not grant default permissions, we preload from this the
- // default permission exceptions lazily to ensure we don't hit the
- // disk on a new user creation.
- mDefaultPermissionGrantPolicy.scheduleReadDefaultPermissionExceptions();
- }
}
private static String getVolumeUuidForPackage(AndroidPackage pkg) {
@@ -4712,13 +4596,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return userState.getUidState(appId);
}
- private void removeAppIdState(@AppIdInt int appId) {
+ private void removeUidState(@AppIdInt int appId, @UserIdInt int userId) {
synchronized (mLock) {
- final int[] userIds = mState.getUserIds();
- for (final int userId : userIds) {
- final UserPermissionState userState = mState.getUserState(userId);
- userState.removeUidState(appId);
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ return;
}
+ userState.removeUidState(appId);
}
}
@@ -4947,17 +4831,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private void onPackageInstalledInternal(@NonNull AndroidPackage pkg,
- @NonNull List<String> grantedPermissions,
- @NonNull List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode,
+ @NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
@UserIdInt int userId) {
- addAllowlistedRestrictedPermissionsInternal(pkg, allowlistedRestrictedPermissions,
+ updatePermissions(pkg.getPackageName(), pkg);
+ addAllowlistedRestrictedPermissionsInternal(pkg,
+ params.getAllowlistedRestrictedPermissions(),
FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
+ final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode();
if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
|| autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
setAutoRevokeExemptedInternal(pkg,
autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
}
- grantRequestedRuntimePermissionsInternal(pkg, grantedPermissions, userId);
+ grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId);
}
private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg,
@@ -4978,25 +4864,34 @@ public class PermissionManagerService extends IPermissionManager.Stub {
removeAllPermissionsInternal(pkg);
}
- private void onPackageStateRemovedInternal(@NonNull String packageName, int appId,
- @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) {
- if (sharedUserPkgs.isEmpty()
- && mPackageManagerInt.getDisabledSystemPackage(packageName) == null) {
- removeAppIdState(appId);
+ private void onPackageUninstalledInternal(@NonNull String packageName, int appId,
+ @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
+ @UserIdInt int userId) {
+ // TODO: Move these checks to check PackageState to be more reliable.
+ // System packages should always have an available APK.
+ if (pkg != null && pkg.isSystem()
+ // We may be fully removing invalid system packages during boot, and in that case we
+ // do want to remove their permission state. So make sure that the package is only
+ // being marked as uninstalled instead of fully removed.
+ && mPackageManagerInt.getPackage(packageName) != null) {
+ // If we are only marking a system package as uninstalled, we need to keep its
+ // pregranted permission state so that it still works once it gets reinstalled, thus
+ // only reset the user modifications to its permission state.
+ resetRuntimePermissionsInternal(pkg, userId);
+ return;
}
- updatePermissions(packageName, null, mDefaultPermissionCallback);
- if (!sharedUserPkgs.isEmpty()) {
+ updatePermissions(packageName, null);
+ if (sharedUserPkgs.isEmpty()) {
+ removeUidState(appId, userId);
+ } else {
// Remove permissions associated with package. Since runtime
// permissions are per user we have to kill the removed package
// or packages running under the shared user of the removed
// package if revoking the permissions requested only by the removed
// package is successful and this causes a change in gids.
- boolean shouldKill = false;
- for (int userId : UserManagerService.getInstance().getUserIds()) {
- final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg,
- sharedUserPkgs, userId);
- shouldKill |= userIdToKill != UserHandle.USER_NULL;
- }
+ final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg,
+ sharedUserPkgs, userId);
+ final boolean shouldKill = userIdToKill != UserHandle.USER_NULL;
// If gids changed, kill all affected packages.
if (shouldKill) {
mHandler.post(() -> {
@@ -5091,7 +4986,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
+ private class PermissionManagerServiceInternalImpl implements PermissionManagerServiceInternal {
@Override
public void systemReady() {
PermissionManagerService.this.systemReady();
@@ -5116,6 +5011,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
public void onUserRemoved(@UserIdInt int userId) {
+ Preconditions.checkArgumentNonNegative(userId, "userId");
PermissionManagerService.this.onUserRemoved(userId);
}
@NonNull
@@ -5140,11 +5036,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName);
}
@Override
- public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
- PermissionManagerService.this
- .updatePermissions(packageName, pkg, mDefaultPermissionCallback);
- }
- @Override
public void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated) {
PermissionManagerService.this
.updateAllPermissions(volumeUuid, sdkUpdated, mDefaultPermissionCallback);
@@ -5200,20 +5091,26 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return matchingPermissions;
}
+ @Nullable
@Override
- public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
- return PermissionManagerService.this.backupRuntimePermissions(user);
+ public byte[] backupRuntimePermissions(@UserIdInt int userId) {
+ Preconditions.checkArgumentNonNegative(userId, "userId");
+ return PermissionManagerService.this.backupRuntimePermissions(userId);
}
@Override
- public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
- PermissionManagerService.this.restoreRuntimePermissions(backup, user);
+ public void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) {
+ Objects.requireNonNull(backup, "backup");
+ Preconditions.checkArgumentNonNegative(userId, "userId");
+ PermissionManagerService.this.restoreRuntimePermissions(backup, userId);
}
@Override
public void restoreDelayedRuntimePermissions(@NonNull String packageName,
- @NonNull UserHandle user) {
- PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user);
+ @UserIdInt int userId) {
+ Objects.requireNonNull(packageName, "packageName");
+ Preconditions.checkArgumentNonNegative(userId, "userId");
+ PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, userId);
}
@Override
@@ -5231,149 +5128,23 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public CheckPermissionDelegate getCheckPermissionDelegate() {
- synchronized (mLock) {
- return mCheckPermissionDelegate;
- }
- }
-
- @Override
- public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) {
- synchronized (mLock) {
- if (delegate != null || mCheckPermissionDelegate != null) {
- PackageManager.invalidatePackageInfoCache();
- }
- mCheckPermissionDelegate = delegate;
- }
- }
-
- @Override
- public void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider) {
- synchronized (mLock) {
- mDefaultBrowserProvider = provider;
- }
- }
-
- @Override
- public void setDefaultBrowser(String packageName, boolean async, boolean doGrant,
- int userId) {
- setDefaultBrowserInternal(packageName, async, doGrant, userId);
- }
-
- @Override
- public void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider) {
- synchronized (mLock) {
- mDefaultDialerProvider = provider;
- }
- }
-
- @Override
- public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) {
- synchronized (mLock) {
- mDefaultHomeProvider = provider;
- }
- }
-
- @Override
- public void setDefaultHome(String packageName, int userId, Consumer<Boolean> callback) {
- if (userId == UserHandle.USER_ALL) {
- return;
- }
- DefaultHomeProvider provider;
- synchronized (mLock) {
- provider = mDefaultHomeProvider;
- }
- if (provider == null) {
- return;
- }
- provider.setDefaultHomeAsync(packageName, userId, callback);
- }
-
- @Override
- public void setDialerAppPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider);
- }
-
- @Override
- public void setLocationExtraPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider);
- }
-
- @Override
- public void setLocationPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider);
- }
-
- @Override
- public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider);
- }
-
- @Override
- public void setSmsAppPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider);
- }
-
- @Override
- public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider);
- }
-
- @Override
- public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider);
- }
-
- @Override
- public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
- mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider);
- }
-
- @Override
- public String getDefaultBrowser(int userId) {
- DefaultBrowserProvider provider;
- synchronized (mLock) {
- provider = mDefaultBrowserProvider;
- }
- return provider != null ? provider.getDefaultBrowser(userId) : null;
- }
-
- @Override
- public String getDefaultDialer(int userId) {
- DefaultDialerProvider provider;
- synchronized (mLock) {
- provider = mDefaultDialerProvider;
- }
- return provider != null ? provider.getDefaultDialer(userId) : null;
- }
-
- @Override
- public String getDefaultHome(int userId) {
- DefaultHomeProvider provider;
- synchronized (mLock) {
- provider = mDefaultHomeProvider;
- }
- return provider != null ? provider.getDefaultHome(userId) : null;
- }
-
- @Override
- public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager(
- packageName, userId);
+ public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName,
+ @Nullable List<String> permissionNames) {
+ Objects.requireNonNull(packageName, "packageName");
+ startShellPermissionIdentityDelegationInternal(uid, packageName, permissionNames);
}
@Override
- public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp(
- packageName, userId);
+ public void stopShellPermissionIdentityDelegation() {
+ stopShellPermissionIdentityDelegationInternal();
}
@Override
- public void onNewUserCreated(int userId) {
+ public void onUserCreated(@UserIdInt int userId) {
+ Preconditions.checkArgumentNonNegative(userId, "userId");
// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
PermissionManagerService.this.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL,
true, mDefaultPermissionCallback);
- mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
}
@Override
@@ -5411,16 +5182,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public void onPackageInstalled(@NonNull AndroidPackage pkg,
- @NonNull List<String> grantedPermissions,
- @NonNull List<String> allowlistedRestrictedPermissions,
- int autoRevokePermissionsMode, @UserIdInt int userId) {
+ @NonNull PackageInstalledParams params, @UserIdInt int userId) {
Objects.requireNonNull(pkg, "pkg");
- Objects.requireNonNull(grantedPermissions, "grantedPermissions");
- Objects.requireNonNull(allowlistedRestrictedPermissions,
- "allowlistedRestrictedPermissions");
+ Objects.requireNonNull(params, "params");
Preconditions.checkArgumentNonNegative(userId, "userId");
- onPackageInstalledInternal(pkg, grantedPermissions, allowlistedRestrictedPermissions,
- autoRevokePermissionsMode, userId);
+ onPackageInstalledInternal(pkg, params, userId);
}
@Override
@@ -5430,11 +5196,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public void onPackageStateRemoved(@NonNull String packageName, int appId,
- @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) {
- Objects.requireNonNull(packageName);
- Objects.requireNonNull(sharedUserPkgs);
- onPackageStateRemovedInternal(packageName, appId, pkg, sharedUserPkgs);
+ public void onPackageUninstalled(@NonNull String packageName, int appId,
+ @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
+ @UserIdInt int userId) {
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs");
+ Preconditions.checkArgumentNonNegative(userId, "userId");
+ onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userId);
}
@Override
@@ -5467,6 +5235,32 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ /**
+ * Callbacks invoked when interesting actions have been taken on a permission.
+ * <p>
+ * NOTE: The current arguments are merely to support the existing use cases. This
+ * needs to be properly thought out with appropriate arguments for each of the
+ * callback methods.
+ */
+ private static class PermissionCallback {
+ public void onGidsChanged(@AppIdInt int appId, @UserIdInt int userId) {}
+ public void onPermissionChanged() {}
+ public void onPermissionGranted(int uid, @UserIdInt int userId) {}
+ public void onInstallPermissionGranted() {}
+ public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {}
+ public void onInstallPermissionRevoked() {}
+ public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {}
+ public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,
+ int uid) {
+ onPermissionUpdated(updatedUserIds, sync);
+ }
+ public void onPermissionRemoved() {}
+ public void onInstallPermissionUpdated() {}
+ public void onInstallPermissionUpdatedNotifyListener(int uid) {
+ onInstallPermissionUpdated();
+ }
+ }
+
private static final class OnPermissionChangeListeners extends Handler {
private static final int MSG_ON_PERMISSIONS_CHANGED = 1;
@@ -5520,6 +5314,102 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
/**
+ * Interface to intercept permission checks and optionally pass through to the original
+ * implementation.
+ */
+ private interface CheckPermissionDelegate {
+ /**
+ * Get the UID whose permission checks is being delegated.
+ *
+ * @return the UID
+ */
+ int getDelegatedUid();
+
+ /**
+ * Check whether the given package has been granted the specified permission.
+ *
+ * @param permissionName the name of the permission to be checked
+ * @param packageName the name of the package to be checked
+ * @param userId the user ID
+ * @param superImpl the original implementation that can be delegated to
+ * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
+ * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise
+ *
+ * @see android.content.pm.PackageManager#checkPermission(String, String)
+ */
+ int checkPermission(@NonNull String permissionName, @NonNull String packageName,
+ @UserIdInt int userId,
+ @NonNull TriFunction<String, String, Integer, Integer> superImpl);
+
+ /**
+ * Check whether the given UID has been granted the specified permission.
+ *
+ * @param permissionName the name of the permission to be checked
+ * @param uid the UID to be checked
+ * @param superImpl the original implementation that can be delegated to
+ * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
+ * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise
+ */
+ int checkUidPermission(@NonNull String permissionName, int uid,
+ BiFunction<String, Integer, Integer> superImpl);
+ }
+
+ private class ShellDelegate implements CheckPermissionDelegate {
+ private final int mDelegatedUid;
+ @NonNull
+ private final String mDelegatedPackageName;
+ @Nullable
+ private final List<String> mDelegatedPermissionNames;
+
+ public ShellDelegate(int delegatedUid, @NonNull String delegatedPackageName,
+ @Nullable List<String> delegatedPermissionNames) {
+ mDelegatedUid = delegatedUid;
+ mDelegatedPackageName = delegatedPackageName;
+ mDelegatedPermissionNames = delegatedPermissionNames;
+ }
+
+ @Override
+ public int getDelegatedUid() {
+ return mDelegatedUid;
+ }
+
+ @Override
+ public int checkPermission(@NonNull String permissionName, @NonNull String packageName,
+ int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
+ if (mDelegatedPackageName.equals(packageName)
+ && isDelegatedPermission(permissionName)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(permissionName, "com.android.shell", userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(permissionName, packageName, userId);
+ }
+
+ @Override
+ public int checkUidPermission(@NonNull String permissionName, int uid,
+ @NonNull BiFunction<String, Integer, Integer> superImpl) {
+ if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(permissionName, Process.SHELL_UID);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return superImpl.apply(permissionName, uid);
+ }
+
+ private boolean isDelegatedPermission(@NonNull String permissionName) {
+ // null permissions means all permissions are targeted
+ return mDelegatedPermissionNames == null
+ || mDelegatedPermissionNames.contains(permissionName);
+ }
+ }
+
+ /**
* Allows injection of services and method responses to facilitate testing.
*
* <p>Test classes can create a mock of this class and pass it to the PermissionManagerService
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 457fe36ca2b8..66e692db2154 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -16,166 +16,45 @@
package com.android.server.pm.permission;
-import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
import android.content.pm.PermissionInfo;
import android.permission.PermissionManagerInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Internal interfaces services.
*
- * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes.
+ * TODO: Move into module.
*/
-public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal
- implements LegacyPermissionDataProvider {
+public interface PermissionManagerServiceInternal extends PermissionManagerInternal,
+ LegacyPermissionDataProvider {
/**
- * Provider for package names.
- */
- public interface PackagesProvider {
-
- /**
- * Gets the packages for a given user.
- * @param userId The user id.
- * @return The package names.
- */
- String[] getPackages(int userId);
- }
-
- /**
- * Provider for package names.
- */
- public interface SyncAdapterPackagesProvider {
-
- /**
- * Gets the sync adapter packages for given authority and user.
- * @param authority The authority.
- * @param userId The user id.
- * @return The package names.
- */
- String[] getPackages(String authority, int userId);
- }
-
- /**
- * Provider for default browser
- */
- public interface DefaultBrowserProvider {
-
- /**
- * Get the package name of the default browser.
- *
- * @param userId the user id
- *
- * @return the package name of the default browser, or {@code null} if none
- */
- @Nullable
- String getDefaultBrowser(@UserIdInt int userId);
-
- /**
- * Set the package name of the default browser.
- *
- * @param packageName package name of the default browser, or {@code null} to remove
- * @param userId the user id
- *
- * @return whether the default browser was successfully set.
- */
- boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId);
-
- /**
- * Set the package name of the default browser asynchronously.
- *
- * @param packageName package name of the default browser, or {@code null} to remove
- * @param userId the user id
- */
- void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId);
- }
-
- /**
- * Provider for default dialer
- */
- public interface DefaultDialerProvider {
-
- /**
- * Get the package name of the default dialer.
- *
- * @param userId the user id
- *
- * @return the package name of the default dialer, or {@code null} if none
- */
- @Nullable
- String getDefaultDialer(@UserIdInt int userId);
- }
-
- /**
- * Provider for default home
+ * Adds a listener for runtime permission state (permissions or flags) changes.
+ *
+ * @param listener The listener.
*/
- public interface DefaultHomeProvider {
-
- /**
- * Get the package name of the default home.
- *
- * @param userId the user id
- *
- * @return the package name of the default home, or {@code null} if none
- */
- @Nullable
- String getDefaultHome(@UserIdInt int userId);
-
- /**
- * Set the package name of the default home.
- *
- * @param packageName package name of the default home, or {@code null} to remove
- * @param userId the user id
- * @param callback the callback made after the default home as been updated
- */
- void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId,
- @NonNull Consumer<Boolean> callback);
- }
+ void addOnRuntimePermissionStateChangedListener(
+ @NonNull OnRuntimePermissionStateChangedListener listener);
/**
- * Callbacks invoked when interesting actions have been taken on a permission.
- * <p>
- * NOTE: The current arguments are merely to support the existing use cases. This
- * needs to be properly thought out with appropriate arguments for each of the
- * callback methods.
+ * Removes a listener for runtime permission state (permissions or flags) changes.
+ *
+ * @param listener The listener.
*/
- public static class PermissionCallback {
- public void onGidsChanged(@AppIdInt int appId, @UserIdInt int userId) {
- }
- public void onPermissionChanged() {
- }
- public void onPermissionGranted(int uid, @UserIdInt int userId) {
- }
- public void onInstallPermissionGranted() {
- }
- public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {
- }
- public void onInstallPermissionRevoked() {
- }
- public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {
- }
- public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,
- int uid) {
- onPermissionUpdated(updatedUserIds, sync);
- }
- public void onPermissionRemoved() {
- }
- public void onInstallPermissionUpdated() {
- }
- public void onInstallPermissionUpdatedNotifyListener(int uid) {
- onInstallPermissionUpdated();
- }
- }
+ void removeOnRuntimePermissionStateChangedListener(
+ @NonNull OnRuntimePermissionStateChangedListener listener);
- public abstract void systemReady();
+ void systemReady();
/**
* Get whether permission review is required for a package.
@@ -185,26 +64,10 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* @return whether permission review is required
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- public abstract boolean isPermissionsReviewRequired(@NonNull String packageName,
+ boolean isPermissionsReviewRequired(@NonNull String packageName,
@UserIdInt int userId);
/**
- * Update permissions when a package changed.
- *
- * <p><ol>
- * <li>Reconsider the ownership of permission</li>
- * <li>Update the state (grant, flags) of the permissions</li>
- * </ol>
- *
- * @param packageName The package that is updated
- * @param pkg The package that is updated, or {@code null} if package is deleted
- * @param allPackages All currently known packages
- * @param callback Callback to call after permission changes
- */
- public abstract void updatePermissions(@NonNull String packageName,
- @Nullable AndroidPackage pkg);
-
- /**
* Update all permissions for all apps.
*
* <p><ol>
@@ -216,7 +79,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* @param allPackages All currently known packages
* @param callback Callback to call after permission changes
*/
- public abstract void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdate);
+ void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdate);
/**
* Reset the runtime permission state changes for a package.
@@ -227,7 +90,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* @param userId the user ID
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- public abstract void resetRuntimePermissions(@NonNull AndroidPackage pkg,
+ void resetRuntimePermissions(@NonNull AndroidPackage pkg,
@UserIdInt int userId);
/**
@@ -236,7 +99,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* @param userId the user ID
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- public abstract void resetAllRuntimePermissions(@UserIdInt int userId);
+ void resetAllRuntimePermissions(@UserIdInt int userId);
/**
* Read legacy permission state from package settings.
@@ -245,7 +108,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* {@code PackageSetting} which is a implementation detail that permission should not know.
* Instead, it should retrieve the legacy state via a defined API.
*/
- public abstract void readLegacyPermissionStateTEMP();
+ void readLegacyPermissionStateTEMP();
/**
* Write legacy permission state to package settings.
@@ -253,12 +116,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
* for permission.
*/
- public abstract void writeLegacyPermissionStateTEMP();
-
- /**
- * Notify that a user has been removed and its permission state should be removed as well.
- */
- public abstract void onUserRemoved(@UserIdInt int userId);
+ void writeLegacyPermissionStateTEMP();
/**
* Get all the permissions granted to a package.
@@ -269,8 +127,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@NonNull
- public abstract Set<String> getGrantedPermissions(@NonNull String packageName,
- @UserIdInt int userId);
+ Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId);
/**
* Get the GIDs of a permission.
@@ -281,7 +138,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@NonNull
- public abstract int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId);
+ int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId);
/**
* Get the packages that have requested an app op permission.
@@ -291,172 +148,46 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@NonNull
- public abstract String[] getAppOpPermissionPackages(@NonNull String permissionName);
+ String[] getAppOpPermissionPackages(@NonNull String permissionName);
/** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
@Nullable
- public abstract Permission getPermissionTEMP(@NonNull String permName);
+ Permission getPermissionTEMP(@NonNull String permName);
/** Get all permissions that have a certain protection */
- public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtection(
+ @NonNull
+ ArrayList<PermissionInfo> getAllPermissionsWithProtection(
@PermissionInfo.Protection int protection);
/** Get all permissions that have certain protection flags */
- public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtectionFlags(
+ @NonNull ArrayList<PermissionInfo> getAllPermissionsWithProtectionFlags(
@PermissionInfo.ProtectionFlags int protectionFlags);
/**
- * Returns the delegate used to influence permission checking.
- *
- * @return The delegate instance.
- */
- public abstract @Nullable CheckPermissionDelegate getCheckPermissionDelegate();
-
- /**
- * Sets the delegate used to influence permission checking.
- *
- * @param delegate A delegate instance or {@code null} to clear.
- */
- public abstract void setCheckPermissionDelegate(@Nullable CheckPermissionDelegate delegate);
-
- /**
- * Sets the dialer application packages provider.
- * @param provider The provider.
- */
- public abstract void setDialerAppPackagesProvider(PackagesProvider provider);
-
- /**
- * Set the location extra packages provider.
- * @param provider The packages provider.
- */
- public abstract void setLocationExtraPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the location provider packages provider.
- * @param provider The packages provider.
- */
- public abstract void setLocationPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the SIM call manager packages provider.
- * @param provider The provider.
- */
- public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the SMS application packages provider.
- * @param provider The provider.
- */
- public abstract void setSmsAppPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the sync adapter packages provider.
- * @param provider The provider.
- */
- public abstract void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider);
-
- /**
- * Sets the Use Open Wifi packages provider.
- * @param provider The packages provider.
- */
- public abstract void setUseOpenWifiAppPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the voice interaction packages provider.
- * @param provider The packages provider.
- */
- public abstract void setVoiceInteractionPackagesProvider(PackagesProvider provider);
-
- /**
- * Sets the default browser provider.
- *
- * @param provider the provider
- */
- public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider);
-
- /**
- * Sets the package name of the default browser provider for the given user.
- *
- * @param packageName The package name of the default browser or {@code null}
- * to clear the default browser
- * @param async If {@code true}, set the default browser asynchronously,
- * otherwise set it synchronously
- * @param doGrant If {@code true} and if {@code packageName} is not {@code null},
- * perform default permission grants on the browser, otherwise skip the
- * default permission grants.
- * @param userId The user to set the default browser for.
- */
- public abstract void setDefaultBrowser(@Nullable String packageName, boolean async,
- boolean doGrant, @UserIdInt int userId);
-
- /**
- * Sets the default dialer provider.
+ * Start delegate the permission identity of the shell UID to the given UID.
*
- * @param provider the provider
+ * @param uid the UID to delegate shell permission identity to
+ * @param packageName the name of the package to delegate shell permission identity to
+ * @param permissionNames the names of the permissions to delegate shell permission identity
+ * for, or {@code null} for all permissions
*/
- public abstract void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider);
-
- /**
- * Sets the default home provider.
- *
- * @param provider the provider
- */
- public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider);
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ void startShellPermissionIdentityDelegation(int uid,
+ @NonNull String packageName, @Nullable List<String> permissionNames);
/**
- * Asynchronously sets the package name of the default home provider for the given user.
+ * Stop delegating the permission identity of the shell UID.
*
- * @param packageName The package name of the default home or {@code null}
- * to clear the default browser
- * @param userId The user to set the default browser for
- * @param callback Invoked after the default home has been set
+ * @see #startShellPermissionIdentityDelegation(int, String, List)
*/
- public abstract void setDefaultHome(@Nullable String packageName, @UserIdInt int userId,
- @NonNull Consumer<Boolean> callback);
-
- /**
- * Returns the default browser package name for the given user.
- */
- @Nullable
- public abstract String getDefaultBrowser(@UserIdInt int userId);
-
- /**
- * Returns the default dialer package name for the given user.
- */
- @Nullable
- public abstract String getDefaultDialer(@UserIdInt int userId);
-
- /**
- * Returns the default home package name for the given user.
- */
- @Nullable
- public abstract String getDefaultHome(@UserIdInt int userId);
-
- /**
- * Requests granting of the default permissions to the current default Use Open Wifi app.
- * @param packageName The default use open wifi package name.
- * @param userId The user for which to grant the permissions.
- */
- public abstract void grantDefaultPermissionsToDefaultSimCallManager(
- @NonNull String packageName, @UserIdInt int userId);
-
- /**
- * Requests granting of the default permissions to the current default Use Open Wifi app.
- * @param packageName The default use open wifi package name.
- * @param userId The user for which to grant the permissions.
- */
- public abstract void grantDefaultPermissionsToDefaultUseOpenWifiApp(
- @NonNull String packageName, @UserIdInt int userId);
-
- /** Called when a new user has been created. */
- public abstract void onNewUserCreated(@UserIdInt int userId);
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ void stopShellPermissionIdentityDelegation();
/**
* Removes invalid permissions which are not {@link PermissionInfo#FLAG_HARD_RESTRICTED} or
* {@link PermissionInfo#FLAG_SOFT_RESTRICTED} from the input.
*/
- public abstract void retainHardAndSoftRestrictedPermissions(
- @NonNull List<String> permissionNames);
+ void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissionNames);
/**
* Read legacy permissions from legacy permission settings.
@@ -465,8 +196,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* {@code LegacyPermissionSettings} which is a implementation detail that permission should not
* know. Instead, it should retrieve the legacy permissions via a defined API.
*/
- public abstract void readLegacyPermissionsTEMP(
- @NonNull LegacyPermissionSettings legacyPermissionSettings);
+ void readLegacyPermissionsTEMP(@NonNull LegacyPermissionSettings legacyPermissionSettings);
/**
* Write legacy permissions to legacy permission settings.
@@ -474,8 +204,23 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
* for permission.
*/
- public abstract void writeLegacyPermissionsTEMP(
- @NonNull LegacyPermissionSettings legacyPermissionSettings);
+ void writeLegacyPermissionsTEMP(@NonNull LegacyPermissionSettings legacyPermissionSettings);
+
+ /**
+ * Callback when a user has been created.
+ *
+ * @param userId the created user ID
+ */
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ void onUserCreated(@UserIdInt int userId);
+
+ /**
+ * Callback when a user has been removed.
+ *
+ * @param userId the removed user ID
+ */
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ void onUserRemoved(@UserIdInt int userId);
/**
* Callback when a package has been added.
@@ -485,23 +230,19 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* @param oldPkg the old package, or {@code null} if none
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- public abstract void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
+ void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
@Nullable AndroidPackage oldPkg);
/**
- * Callback when a package has been installed for certain users.
+ * Callback when a package has been installed for a user.
*
* @param pkg the installed package
- * @param grantedPermissions the permissions to be granted
- * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted
- * @param autoRevokePermissionsMode the auto revoke permissions mode for this package
+ * @param params the parameters passed in for package installation
* @param userId the user ID this package is installed for
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- public abstract void onPackageInstalled(@NonNull AndroidPackage pkg,
- @NonNull List<String> grantedPermissions,
- @NonNull List<String> allowlistedRestrictedPermissions,
- int autoRevokePermissionsMode, @UserIdInt int userId);
+ void onPackageInstalled(@NonNull AndroidPackage pkg, @NonNull PackageInstalledParams params,
+ @UserIdInt int userId);
/**
* Callback when a package has been removed.
@@ -509,19 +250,25 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* @param pkg the removed package
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- public abstract void onPackageRemoved(@NonNull AndroidPackage pkg);
+ void onPackageRemoved(@NonNull AndroidPackage pkg);
/**
- * Callback when the state for a package has been removed.
+ * Callback when a package has been uninstalled.
+ * <p>
+ * The package may have been fully removed from the system, or only marked as uninstalled for
+ * this user but still instlaled for other users.
+ *
+ * TODO: Pass PackageState instead.
*
- * @param packageName the name of the removed package
- * @param appId the app ID of the removed package
- * @param pkg the removed package, or {@code null} if unavailable
+ * @param packageName the name of the uninstalled package
+ * @param appId the app ID of the uninstalled package
+ * @param pkg the uninstalled package, or {@code null} if unavailable
* @param sharedUserPkgs the packages that are in the same shared user
+ * @param userId the user ID the package is uninstalled for
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
- public abstract void onPackageStateRemoved(@NonNull String packageName, int appId,
- @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs);
+ void onPackageUninstalled(@NonNull String packageName, int appId, @Nullable AndroidPackage pkg,
+ @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId);
/**
* Check whether a permission can be propagated to instant app.
@@ -529,5 +276,149 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
* @param permissionName the name of the permission
* @return whether the permission can be propagated
*/
- public abstract boolean canPropagatePermissionToInstantApp(@NonNull String permissionName);
+ boolean canPropagatePermissionToInstantApp(@NonNull String permissionName);
+
+ /**
+ * Listener for package permission state (permissions or flags) changes.
+ */
+ interface OnRuntimePermissionStateChangedListener {
+
+ /**
+ * Called when the runtime permission state (permissions or flags) changed.
+ *
+ * @param packageName The package for which the change happened.
+ * @param userId the user id for which the change happened.
+ */
+ @Nullable
+ void onRuntimePermissionStateChanged(@NonNull String packageName,
+ @UserIdInt int userId);
+ }
+
+ /**
+ * The permission-related parameters passed in for package installation.
+ *
+ * @see android.content.pm.PackageInstaller.SessionParams
+ */
+ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+ final class PackageInstalledParams {
+ /**
+ * A static instance whose parameters are all in their default state.
+ */
+ public static final PackageInstalledParams DEFAULT = new Builder().build();
+
+ @NonNull
+ private final List<String> mGrantedPermissions;
+ @NonNull
+ private final List<String> mAllowlistedRestrictedPermissions;
+ @NonNull
+ private final int mAutoRevokePermissionsMode;
+
+ private PackageInstalledParams(@NonNull List<String> grantedPermissions,
+ @NonNull List<String> allowlistedRestrictedPermissions,
+ int autoRevokePermissionsMode) {
+ mGrantedPermissions = grantedPermissions;
+ mAllowlistedRestrictedPermissions = allowlistedRestrictedPermissions;
+ mAutoRevokePermissionsMode = autoRevokePermissionsMode;
+ }
+
+ /**
+ * Get the permissions to be granted.
+ *
+ * @return the permissions to be granted
+ */
+ @NonNull
+ public List<String> getGrantedPermissions() {
+ return mGrantedPermissions;
+ }
+
+ /**
+ * Get the restricted permissions to be allowlisted.
+ *
+ * @return the restricted permissions to be allowlisted
+ */
+ @NonNull
+ public List<String> getAllowlistedRestrictedPermissions() {
+ return mAllowlistedRestrictedPermissions;
+ }
+
+ /**
+ * Get the mode for auto revoking permissions.
+ *
+ * @return the mode for auto revoking permissions
+ */
+ public int getAutoRevokePermissionsMode() {
+ return mAutoRevokePermissionsMode;
+ }
+
+ /**
+ * Builder class for {@link PackageInstalledParams}.
+ */
+ public static final class Builder {
+ @NonNull
+ private List<String> mGrantedPermissions = Collections.emptyList();
+ @NonNull
+ private List<String> mAllowlistedRestrictedPermissions = Collections.emptyList();
+ @NonNull
+ private int mAutoRevokePermissionsMode = AppOpsManager.MODE_DEFAULT;
+
+ /**
+ * Set the permissions to be granted.
+ *
+ * @param grantedPermissions the permissions to be granted
+ *
+ * @see android.content.pm.PackageInstaller.SessionParams#setGrantedRuntimePermissions(
+ * java.lang.String[])
+ */
+ public void setGrantedPermissions(@NonNull List<String> grantedPermissions) {
+ Objects.requireNonNull(grantedPermissions);
+ mGrantedPermissions = new ArrayList<>(grantedPermissions);
+ }
+
+ /**
+ * Set the restricted permissions to be allowlisted.
+ * <p>
+ * Permissions that are not restricted are ignored, so one can just pass in all
+ * requested permissions of a package to get all its restricted permissions allowlisted.
+ *
+ * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted
+ *
+ * @see android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)
+ */
+ public void setAllowlistedRestrictedPermissions(
+ @NonNull List<String> allowlistedRestrictedPermissions) {
+ Objects.requireNonNull(mGrantedPermissions);
+ mAllowlistedRestrictedPermissions = new ArrayList<>(
+ allowlistedRestrictedPermissions);
+ }
+
+ /**
+ * Set the mode for auto revoking permissions.
+ * <p>
+ * {@link AppOpsManager#MODE_ALLOWED} means the system is allowed to auto revoke
+ * permissions from this package, and {@link AppOpsManager#MODE_IGNORED} means this
+ * package should be ignored when auto revoking permissions.
+ * {@link AppOpsManager#MODE_DEFAULT} means no changes will be made to the auto revoke
+ * mode of this package.
+ *
+ * @param autoRevokePermissionsMode the mode for auto revoking permissions
+ *
+ * @see android.content.pm.PackageInstaller.SessionParams#setAutoRevokePermissionsMode(
+ * boolean)
+ */
+ public void setAutoRevokePermissionsMode(int autoRevokePermissionsMode) {
+ mAutoRevokePermissionsMode = autoRevokePermissionsMode;
+ }
+
+ /**
+ * Build a new instance of {@link PackageInstalledParams}.
+ *
+ * @return the {@link PackageInstalledParams} built
+ */
+ @NonNull
+ public PackageInstalledParams build() {
+ return new PackageInstalledParams(mGrantedPermissions,
+ mAllowlistedRestrictedPermissions, mAutoRevokePermissionsMode);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 321bb8c0251d..cc1f8d620d1f 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -158,7 +158,6 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
// switch there is no need to register for a callback.
boolean shouldListenToLidSwitch = false;
- final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
// The set of Sensor(s) that this instance should register to receive SensorEvent(s) from.
final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
@@ -182,19 +181,10 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
List<SensorCondition> sensorConditions = conditions.getSensor();
for (int j = 0; j < sensorConditions.size(); j++) {
SensorCondition sensorCondition = sensorConditions.get(j);
- final int expectedSensorType = sensorCondition.getType().intValue();
+ final String expectedSensorType = sensorCondition.getType();
final String expectedSensorName = sensorCondition.getName();
- List<Sensor> sensors = sensorManager.getSensorList(expectedSensorType);
- Sensor foundSensor = null;
- for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) {
- Sensor sensor = sensors.get(sensorIndex);
- if (sensor.getName().equals(expectedSensorName)) {
- foundSensor = sensor;
- break;
- }
- }
-
+ final Sensor foundSensor = findSensor(expectedSensorType, expectedSensorName);
if (foundSensor == null) {
throw new IllegalStateException("Failed to find Sensor with type: "
+ expectedSensorType + " and name: " + expectedSensorName);
@@ -221,12 +211,33 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
inputManager.registerLidSwitchCallback(this);
}
+ final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
for (int i = 0; i < sensorsToListenTo.size(); i++) {
Sensor sensor = sensorsToListenTo.valueAt(i);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
}
+ @Nullable
+ private Sensor findSensor(String type, String name) {
+ final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
+ final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
+ for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) {
+ final Sensor sensor = sensors.get(sensorIndex);
+ final String sensorType = sensor.getStringType();
+ final String sensorName = sensor.getName();
+
+ if (sensorType == null || sensorName == null) {
+ continue;
+ }
+
+ if (sensorType.equals(type) && sensorName.equals(name)) {
+ return sensor;
+ }
+ }
+ return null;
+ }
+
@Override
public void setListener(Listener listener) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
new file mode 100644
index 000000000000..84ac12497e71
--- /dev/null
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.policy;
+
+import static android.view.KeyEvent.KEYCODE_POWER;
+
+import android.os.SystemClock;
+import android.util.SparseLongArray;
+import android.view.KeyEvent;
+
+import com.android.internal.util.ToBooleanFunction;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Handles a mapping of two keys combination.
+ */
+public class KeyCombinationManager {
+ private static final String TAG = "KeyCombinationManager";
+
+ // Store the received down time of keycode.
+ private final SparseLongArray mDownTimes = new SparseLongArray(2);
+ private final ArrayList<TwoKeysCombinationRule> mRules = new ArrayList();
+
+ // Selected rules according to current key down.
+ private final ArrayList<TwoKeysCombinationRule> mActiveRules = new ArrayList();
+ // The rule has been triggered by current keys.
+ private TwoKeysCombinationRule mTriggeredRule;
+
+ // Keys in a key combination must be pressed within this interval of each other.
+ private static final long COMBINE_KEY_DELAY_MILLIS = 150;
+
+ /**
+ * Rule definition for two keys combination.
+ * E.g : define volume_down + power key.
+ * <pre class="prettyprint">
+ * TwoKeysCombinationRule rule =
+ * new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
+ * boolean preCondition() { // check if it needs to intercept key }
+ * void execute() { // trigger action }
+ * void cancel() { // cancel action }
+ * };
+ * </pre>
+ */
+ abstract static class TwoKeysCombinationRule {
+ private int mKeyCode1;
+ private int mKeyCode2;
+
+ TwoKeysCombinationRule(int keyCode1, int keyCode2) {
+ mKeyCode1 = keyCode1;
+ mKeyCode2 = keyCode2;
+ }
+
+ boolean preCondition() {
+ return true;
+ }
+
+ boolean shouldInterceptKey(int keyCode) {
+ return preCondition() && (keyCode == mKeyCode1 || keyCode == mKeyCode2);
+ }
+
+ boolean shouldInterceptKeys(SparseLongArray downTimes) {
+ final long now = SystemClock.uptimeMillis();
+ if (downTimes.get(mKeyCode1) > 0
+ && downTimes.get(mKeyCode2) > 0
+ && now <= downTimes.get(mKeyCode1) + COMBINE_KEY_DELAY_MILLIS
+ && now <= downTimes.get(mKeyCode2) + COMBINE_KEY_DELAY_MILLIS) {
+ return true;
+ }
+ return false;
+ }
+
+ abstract void execute();
+ abstract void cancel();
+
+ @Override
+ public String toString() {
+ return "KeyCode1 = " + KeyEvent.keyCodeToString(mKeyCode1)
+ + ", KeyCode2 = " + KeyEvent.keyCodeToString(mKeyCode2);
+ }
+ }
+
+ public KeyCombinationManager() {
+ }
+
+ void addRule(TwoKeysCombinationRule rule) {
+ mRules.add(rule);
+ }
+
+ /**
+ * Check if the key event could be triggered by combine key rule before dispatching to a window.
+ */
+ void interceptKey(KeyEvent event, boolean interactive) {
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ final int keyCode = event.getKeyCode();
+ final int count = mActiveRules.size();
+ final long eventTime = event.getEventTime();
+
+ if (interactive && down) {
+ if (mDownTimes.size() > 0) {
+ if (count > 0
+ && eventTime > mDownTimes.valueAt(0) + COMBINE_KEY_DELAY_MILLIS) {
+ // exceed time from first key down.
+ forAllRules(mActiveRules, (rule)-> rule.cancel());
+ mActiveRules.clear();
+ return;
+ } else if (count == 0) { // has some key down but no active rule exist.
+ return;
+ }
+ }
+
+ if (mDownTimes.get(keyCode) == 0) {
+ mDownTimes.put(keyCode, eventTime);
+ } else {
+ // ignore old key, maybe a repeat key.
+ return;
+ }
+
+ if (mDownTimes.size() == 1) {
+ mTriggeredRule = null;
+ // check first key and pick active rules.
+ forAllRules(mRules, (rule)-> {
+ if (rule.shouldInterceptKey(keyCode)) {
+ mActiveRules.add(rule);
+ }
+ });
+ } else {
+ // Ignore if rule already triggered.
+ if (mTriggeredRule != null) {
+ return;
+ }
+
+ // check if second key can trigger rule, or remove the non-match rule.
+ forAllActiveRules((rule) -> {
+ if (!rule.shouldInterceptKeys(mDownTimes)) {
+ return false;
+ }
+ rule.execute();
+ mTriggeredRule = rule;
+ return true;
+ });
+ mActiveRules.clear();
+ if (mTriggeredRule != null) {
+ mActiveRules.add(mTriggeredRule);
+ }
+ }
+ } else {
+ mDownTimes.delete(keyCode);
+ for (int index = count - 1; index >= 0; index--) {
+ final TwoKeysCombinationRule rule = mActiveRules.get(index);
+ if (rule.shouldInterceptKey(keyCode)) {
+ rule.cancel();
+ mActiveRules.remove(index);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the interceptTimeout to tell InputDispatcher when is ready to deliver to window.
+ */
+ long getKeyInterceptTimeout(int keyCode) {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
+ return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ }
+ return 0;
+ }
+
+ /**
+ * True if the key event had been handled.
+ */
+ boolean isKeyConsumed(KeyEvent event) {
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
+ return false;
+ }
+ return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
+ }
+
+ /**
+ * True if power key is the candidate.
+ */
+ boolean isPowerKeyIntercepted() {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
+ // return false if only if power key pressed.
+ return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+ }
+ return false;
+ }
+
+ /**
+ * Traverse each item of rules.
+ */
+ private void forAllRules(
+ ArrayList<TwoKeysCombinationRule> rules, Consumer<TwoKeysCombinationRule> callback) {
+ final int count = rules.size();
+ for (int index = 0; index < count; index++) {
+ final TwoKeysCombinationRule rule = rules.get(index);
+ callback.accept(rule);
+ }
+ }
+
+ /**
+ * Traverse each item of active rules until some rule can be applied, otherwise return false.
+ */
+ private boolean forAllActiveRules(ToBooleanFunction<TwoKeysCombinationRule> callback) {
+ final int count = mActiveRules.size();
+ for (int index = 0; index < count; index++) {
+ final TwoKeysCombinationRule rule = mActiveRules.get(index);
+ if (callback.apply(rule)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8beec35ebc64..75868e3216cc 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -35,7 +35,13 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_OFF;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
+import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
+import static android.view.KeyEvent.KEYCODE_POWER;
import static android.view.KeyEvent.KEYCODE_UNKNOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
@@ -202,6 +208,7 @@ import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
@@ -405,7 +412,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private boolean mEnableCarDockHomeCapture = true;
boolean mBootMessageNeedsHiding;
- KeyguardServiceDelegate mKeyguardDelegate;
+ private KeyguardServiceDelegate mKeyguardDelegate;
private boolean mKeyguardBound;
final Runnable mWindowManagerDrawCallback = new Runnable() {
@Override
@@ -422,8 +429,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
};
- GlobalActions mGlobalActions;
- Handler mHandler;
+ private GlobalActions mGlobalActions;
+ private Handler mHandler;
// FIXME This state is shared between the input reader and handler thread.
// Technically it's broken and buggy but it has been like this for many years
@@ -547,34 +554,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private boolean mGoToSleepOnButtonPressTheaterMode;
// Screenshot trigger states
- // Time to volume and power must be pressed within this interval of each other.
- private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;
// Increase the chord delay when taking a screenshot from the keyguard
private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f;
- private boolean mScreenshotChordEnabled;
- private boolean mScreenshotChordVolumeDownKeyTriggered;
- private long mScreenshotChordVolumeDownKeyTime;
- private boolean mScreenshotChordVolumeDownKeyConsumed;
- private boolean mA11yShortcutChordVolumeUpKeyTriggered;
- private long mA11yShortcutChordVolumeUpKeyTime;
- private boolean mA11yShortcutChordVolumeUpKeyConsumed;
-
- private boolean mScreenshotChordPowerKeyTriggered;
- private long mScreenshotChordPowerKeyTime;
// Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
- private int mRingerToggleChord = VOLUME_HUSH_OFF;
+ int mRingerToggleChord = VOLUME_HUSH_OFF;
private static final long BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS = 1000;
- private boolean mBugreportTvKey1Pressed;
- private boolean mBugreportTvKey2Pressed;
- private boolean mBugreportTvScheduled;
-
- private boolean mAccessibilityTvKey1Pressed;
- private boolean mAccessibilityTvKey2Pressed;
- private boolean mAccessibilityTvScheduled;
-
/* The number of steps between min and max brightness */
private static final int BRIGHTNESS_STEPS = 10;
@@ -603,6 +590,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
+ private KeyCombinationManager mKeyCombinationManager;
+
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
@@ -900,15 +889,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerFuncs.onPowerKeyDown(interactive);
- // Latch power key state to detect screenshot chord.
- if (interactive && !mScreenshotChordPowerKeyTriggered
- && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
- mScreenshotChordPowerKeyTriggered = true;
- mScreenshotChordPowerKeyTime = event.getDownTime();
- interceptScreenshotChord();
- interceptRingerToggleChord();
- }
-
// Stop ringing or end call if configured to do so when power is pressed.
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
@@ -946,9 +926,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
- mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
- || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted
- || handledByPowerManager;
+ mPowerKeyHandled = hungUp || gesturedServiceIntercepted
+ || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
if (!mPowerKeyHandled) {
if (interactive) {
// When interactive, we're already awake.
@@ -1004,8 +983,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
final boolean handled = canceled || mPowerKeyHandled;
- mScreenshotChordPowerKeyTriggered = false;
- cancelPendingScreenshotChordAction();
cancelPendingPowerKeyAction();
if (!handled) {
@@ -1315,52 +1292,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void interceptScreenshotChord() {
- if (mScreenshotChordEnabled
- && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
- && !mA11yShortcutChordVolumeUpKeyTriggered) {
- final long now = SystemClock.uptimeMillis();
- if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
- && now <= mScreenshotChordPowerKeyTime
- + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
- mScreenshotChordVolumeDownKeyConsumed = true;
- cancelPendingPowerKeyAction();
- mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
- mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
- mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
- }
- }
+ mHandler.removeCallbacks(mScreenshotRunnable);
+ mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
+ mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
+ mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
}
private void interceptAccessibilityShortcutChord() {
- if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())
- && mScreenshotChordVolumeDownKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered
- && !mScreenshotChordPowerKeyTriggered) {
- final long now = SystemClock.uptimeMillis();
- if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
- && now <= mA11yShortcutChordVolumeUpKeyTime
- + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
- mScreenshotChordVolumeDownKeyConsumed = true;
- mA11yShortcutChordVolumeUpKeyConsumed = true;
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
- getAccessibilityShortcutTimeout());
- }
- }
+ mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
+ getAccessibilityShortcutTimeout());
}
private void interceptRingerToggleChord() {
- if (mRingerToggleChord != Settings.Secure.VOLUME_HUSH_OFF
- && mScreenshotChordPowerKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered) {
- final long now = SystemClock.uptimeMillis();
- if (now <= mA11yShortcutChordVolumeUpKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
- && now <= mScreenshotChordPowerKeyTime
- + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
- mA11yShortcutChordVolumeUpKeyConsumed = true;
- cancelPendingPowerKeyAction();
-
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
- getRingerToggleChordDelay());
- }
- }
+ mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
+ getRingerToggleChordDelay());
}
private long getAccessibilityShortcutTimeout() {
@@ -1942,9 +1889,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_safeModeEnabledVibePattern);
- mScreenshotChordEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableScreenshotChord);
-
mGlobalKeyManager = new GlobalKeyManager(mContext);
// Controls rotation and the like.
@@ -1980,6 +1924,92 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged();
}
});
+ initKeyCombinationRules();
+ }
+
+ private void initKeyCombinationRules() {
+ mKeyCombinationManager = new KeyCombinationManager();
+ final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableScreenshotChord);
+
+ if (screenshotChordEnabled) {
+ mKeyCombinationManager.addRule(
+ new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
+ @Override
+ void execute() {
+ cancelPendingPowerKeyAction();
+ interceptScreenshotChord();
+ }
+ @Override
+ void cancel() {
+ cancelPendingScreenshotChordAction();
+ }
+ });
+ }
+
+ mKeyCombinationManager.addRule(
+ new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP) {
+ @Override
+ boolean preCondition() {
+ return mAccessibilityShortcutController
+ .isAccessibilityShortcutAvailable(isKeyguardLocked());
+ }
+ @Override
+ void execute() {
+ interceptAccessibilityShortcutChord();
+ }
+ @Override
+ void cancel() {
+ cancelPendingAccessibilityShortcutAction();
+ }
+ });
+
+ mKeyCombinationManager.addRule(
+ new TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) {
+ @Override
+ boolean preCondition() {
+ return mRingerToggleChord != VOLUME_HUSH_OFF;
+ }
+ @Override
+ void execute() {
+ cancelPendingPowerKeyAction();
+ interceptRingerToggleChord();
+ }
+ @Override
+ void cancel() {
+ cancelPendingRingerToggleChordAction();
+ }
+ });
+
+ if (mHasFeatureLeanback) {
+ mKeyCombinationManager.addRule(
+ new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
+ @Override
+ void execute() {
+ cancelPendingBackKeyAction();
+ interceptAccessibilityGestureTv();
+ }
+
+ @Override
+ void cancel() {
+ cancelAccessibilityGestureTv();
+ }
+ });
+
+ mKeyCombinationManager.addRule(
+ new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
+ @Override
+ void execute() {
+ cancelPendingBackKeyAction();
+ interceptBugreportGestureTv();
+ }
+
+ @Override
+ void cancel() {
+ cancelBugreportGestureTv();
+ }
+ });
+ }
}
/**
@@ -2552,70 +2582,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ repeatCount + " keyguardOn=" + keyguardOn + " canceled=" + canceled);
}
- // If we think we might have a volume down & power key chord on the way
- // but we're not sure, then tell the dispatcher to wait a little while and
- // try again later before dispatching.
- if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
- if (mScreenshotChordVolumeDownKeyTriggered && !mScreenshotChordPowerKeyTriggered) {
- final long now = SystemClock.uptimeMillis();
- final long timeoutTime = mScreenshotChordVolumeDownKeyTime
- + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
- if (now < timeoutTime) {
- return timeoutTime - now;
- }
- }
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
- && mScreenshotChordVolumeDownKeyConsumed) {
- if (!down) {
- mScreenshotChordVolumeDownKeyConsumed = false;
- }
- return -1;
- }
- }
-
- // If an accessibility shortcut might be partially complete, hold off dispatching until we
- // know if it is complete or not
- if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)
- && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
- if (mScreenshotChordVolumeDownKeyTriggered ^ mA11yShortcutChordVolumeUpKeyTriggered) {
- final long now = SystemClock.uptimeMillis();
- final long timeoutTime = (mScreenshotChordVolumeDownKeyTriggered
- ? mScreenshotChordVolumeDownKeyTime : mA11yShortcutChordVolumeUpKeyTime)
- + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
- if (now < timeoutTime) {
- return timeoutTime - now;
- }
- }
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mScreenshotChordVolumeDownKeyConsumed) {
- if (!down) {
- mScreenshotChordVolumeDownKeyConsumed = false;
- }
- return -1;
- }
- if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) {
- if (!down) {
- mA11yShortcutChordVolumeUpKeyConsumed = false;
- }
- return -1;
- }
+ if (mKeyCombinationManager.isKeyConsumed(event)) {
+ return -1;
}
- // If a ringer toggle chord could be on the way but we're not sure, then tell the dispatcher
- // to wait a little while and try again later before dispatching.
- if (mRingerToggleChord != VOLUME_HUSH_OFF && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
- if (mA11yShortcutChordVolumeUpKeyTriggered && !mScreenshotChordPowerKeyTriggered) {
- final long now = SystemClock.uptimeMillis();
- final long timeoutTime = mA11yShortcutChordVolumeUpKeyTime
- + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
- if (now < timeoutTime) {
- return timeoutTime - now;
- }
- }
- if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) {
- if (!down) {
- mA11yShortcutChordVolumeUpKeyConsumed = false;
- }
- return -1;
+ if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
+ final long now = SystemClock.uptimeMillis();
+ final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode);
+ if (now < interceptTimeout) {
+ return interceptTimeout - now;
}
}
@@ -2774,8 +2749,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) {
// Pass through keyboard navigation keys.
return 0;
- } else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) {
- return -1;
} else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) {
if (!down) {
mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
@@ -2978,53 +2951,30 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/**
* TV only: recognizes a remote control gesture for capturing a bug report.
*/
- private boolean interceptBugreportGestureTv(int keyCode, boolean down) {
+ private void interceptBugreportGestureTv() {
+ mHandler.removeMessages(MSG_BUGREPORT_TV);
// The bugreport capture chord is a long press on DPAD CENTER and BACK simultaneously.
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
- mBugreportTvKey1Pressed = down;
- } else if (keyCode == KeyEvent.KEYCODE_BACK) {
- mBugreportTvKey2Pressed = down;
- }
-
- if (mBugreportTvKey1Pressed && mBugreportTvKey2Pressed) {
- if (!mBugreportTvScheduled) {
- mBugreportTvScheduled = true;
- Message msg = Message.obtain(mHandler, MSG_BUGREPORT_TV);
- msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS);
- }
- } else if (mBugreportTvScheduled) {
- mHandler.removeMessages(MSG_BUGREPORT_TV);
- mBugreportTvScheduled = false;
- }
+ Message msg = Message.obtain(mHandler, MSG_BUGREPORT_TV);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS);
+ }
- return mBugreportTvScheduled;
+ private void cancelBugreportGestureTv() {
+ mHandler.removeMessages(MSG_BUGREPORT_TV);
}
/**
* TV only: recognizes a remote control gesture as Accessibility shortcut.
* Shortcut: Long press (BACK + DPAD_DOWN)
*/
- private boolean interceptAccessibilityGestureTv(int keyCode, boolean down) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- mAccessibilityTvKey1Pressed = down;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- mAccessibilityTvKey2Pressed = down;
- }
-
- if (mAccessibilityTvKey1Pressed && mAccessibilityTvKey2Pressed) {
- if (!mAccessibilityTvScheduled) {
- mAccessibilityTvScheduled = true;
- Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV);
- msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, getAccessibilityShortcutTimeout());
- }
- } else if (mAccessibilityTvScheduled) {
- mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
- mAccessibilityTvScheduled = false;
- }
-
- return mAccessibilityTvScheduled;
+ private void interceptAccessibilityGestureTv() {
+ mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
+ Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, getAccessibilityShortcutTimeout());
+ }
+ private void cancelAccessibilityGestureTv() {
+ mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
}
private void requestBugreportForTv() {
@@ -3547,16 +3497,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final int displayId = event.getDisplayId();
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
- // If screen is off then we treat the case where the keyguard is open but hidden
- // the same as if it were open and in front.
- // This will prevent any keys other than the power button from waking the screen
- // when the keyguard is hidden by another activity.
- final boolean keyguardActive = (mKeyguardDelegate == null ? false :
- (interactive ?
- isKeyguardShowingAndNotOccluded() :
- mKeyguardDelegate.isShowing()));
-
if (DEBUG_INPUT) {
+ // If screen is off then we treat the case where the keyguard is open but hidden
+ // the same as if it were open and in front.
+ // This will prevent any keys other than the power button from waking the screen
+ // when the keyguard is hidden by another activity.
+ final boolean keyguardActive = (mKeyguardDelegate != null
+ && (interactive ? isKeyguardShowingAndNotOccluded() :
+ mKeyguardDelegate.isShowing()));
Log.d(TAG, "interceptKeyTq keycode=" + keyCode
+ " interactive=" + interactive + " keyguardActive=" + keyguardActive
+ " policyFlags=" + Integer.toHexString(policyFlags));
@@ -3581,7 +3529,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Reset the pending key
mPendingWakeKey = PENDING_KEY_NULL;
}
- } else if (!interactive && shouldDispatchInputWhenNonInteractive(displayId, keyCode)) {
+ } else if (shouldDispatchInputWhenNonInteractive(displayId, keyCode)) {
// If we're currently dozing with the screen on and the keyguard showing, pass the key
// to the application but preserve its wake key status to make sure we still move
// from dozing to fully interactive if we would normally go from off to fully
@@ -3613,6 +3561,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return result;
}
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+ mKeyCombinationManager.interceptKey(event, interactive);
+ }
+
// Enable haptics if down and virtual key without multiple repetitions. If this is a hard
// virtual key such as a navigation bar button, only vibrate if flag is enabled.
final boolean isNavBarVirtKey = ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0);
@@ -3640,46 +3592,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
- if (down) {
- // Any activity on the vol down button stops the ringer toggle shortcut
- cancelPendingRingerToggleChordAction();
-
- if (interactive && !mScreenshotChordVolumeDownKeyTriggered
- && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
- mScreenshotChordVolumeDownKeyTriggered = true;
- mScreenshotChordVolumeDownKeyTime = event.getDownTime();
- mScreenshotChordVolumeDownKeyConsumed = false;
- cancelPendingPowerKeyAction();
- interceptScreenshotChord();
- interceptAccessibilityShortcutChord();
- }
- } else {
- mScreenshotChordVolumeDownKeyTriggered = false;
- cancelPendingScreenshotChordAction();
- cancelPendingAccessibilityShortcutAction();
- }
- } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- if (down) {
- if (interactive && !mA11yShortcutChordVolumeUpKeyTriggered
- && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
- mA11yShortcutChordVolumeUpKeyTriggered = true;
- mA11yShortcutChordVolumeUpKeyTime = event.getDownTime();
- mA11yShortcutChordVolumeUpKeyConsumed = false;
- cancelPendingPowerKeyAction();
- cancelPendingScreenshotChordAction();
- cancelPendingRingerToggleChordAction();
-
- interceptAccessibilityShortcutChord();
- interceptRingerToggleChord();
- }
- } else {
- mA11yShortcutChordVolumeUpKeyTriggered = false;
- cancelPendingScreenshotChordAction();
- cancelPendingAccessibilityShortcutAction();
- cancelPendingRingerToggleChordAction();
- }
- }
if (down) {
sendSystemKeyToStatusBarAsync(event.getKeyCode());
@@ -3784,7 +3696,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
// Any activity on the power button stops the accessibility shortcut
- cancelPendingAccessibilityShortcutAction();
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
@@ -3922,22 +3833,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- // Intercept the Accessibility keychord for TV (DPAD_DOWN + Back) before the keyevent is
- // processed through interceptKeyEventBeforeDispatch since Talkback may consume this event
- // before it has a chance to reach that method.
- if (mHasFeatureLeanback) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_BACK: {
- boolean handled = interceptAccessibilityGestureTv(keyCode, down);
- if (handled) {
- result &= ~ACTION_PASS_TO_USER;
- }
- break;
- }
- }
- }
-
// Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())) {
switch (keyCode) {
@@ -5388,14 +5283,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty());
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
- if (mHasFeatureLeanback) {
- pw.print(prefix);
- pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed);
- pw.print(prefix);
- pw.print("mAccessibilityTvKey2Pressed="); pw.println(mAccessibilityTvKey2Pressed);
- pw.print(prefix);
- pw.print("mAccessibilityTvScheduled="); pw.println(mAccessibilityTvScheduled);
- }
mGlobalKeyManager.dump(prefix, pw);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 1e4e0a6a04bc..68d038bb5cf6 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -368,12 +368,6 @@ public class BatterySaverController implements BatterySaverPolicyListener {
}
}
- boolean setAdaptivePolicyLocked(String settings, String deviceSpecificSettings, int reason) {
- return setAdaptivePolicyLocked(
- BatterySaverPolicy.Policy.fromSettings(settings, deviceSpecificSettings),
- reason);
- }
-
boolean setAdaptivePolicyLocked(BatterySaverPolicyConfig config, int reason) {
return setAdaptivePolicyLocked(BatterySaverPolicy.Policy.fromConfig(config), reason);
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 1883f4e4e86c..8eb66cdee5cd 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -26,9 +26,11 @@ import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.KeyValueListParser;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
@@ -47,6 +49,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* Class to decide whether to turn on battery saver mode for specific services.
@@ -56,60 +59,81 @@ import java.util.Objects;
*
* Test: atest com.android.server.power.batterysaver.BatterySaverPolicyTest
*/
-public class BatterySaverPolicy extends ContentObserver {
+public class BatterySaverPolicy extends ContentObserver implements
+ DeviceConfig.OnPropertiesChangedListener {
private static final String TAG = "BatterySaverPolicy";
static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
- private static final String KEY_GPS_MODE = "gps_mode";
- private static final String KEY_VIBRATION_DISABLED = "vibration_disabled";
- private static final String KEY_ANIMATION_DISABLED = "animation_disabled";
- private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled";
+ @VisibleForTesting
+ static final String KEY_LOCATION_MODE = "location_mode";
+ @VisibleForTesting
+ static final String KEY_DISABLE_VIBRATION = "disable_vibration";
+ @VisibleForTesting
+ static final String KEY_DISABLE_ANIMATION = "disable_animation";
+ @VisibleForTesting
+ static final String KEY_DISABLE_SOUNDTRIGGER = "disable_soundtrigger";
/**
- * Disable turning on the network firewall when Battery Saver is turned on.
- * If set to false, the firewall WILL be turned on when Battery Saver is turned on.
- * If set to true, the firewall WILL NOT be turned on when Battery Saver is turned on.
+ * Turn on the network firewall when Battery Saver is turned on.
+ * If set to false, the firewall WILL NOT be turned on when Battery Saver is turned on.
+ * If set to true, the firewall WILL be turned on when Battery Saver is turned on.
*/
- private static final String KEY_ACTIVATE_FIREWALL_DISABLED = "firewall_disabled";
+ @VisibleForTesting
+ static final String KEY_ENABLE_FIREWALL = "enable_firewall";
/**
- * Disable turning on the special low power screen brightness dimming when Battery Saver is
+ * Turn on the special low power screen brightness dimming when Battery Saver is
* turned on.
- * If set to false, the screen brightness dimming WILL be turned on by Battery Saver.
- * If set to true, the screen brightness WILL NOT be turned on by Battery Saver.
+ * If set to false, the screen brightness dimming WILL NOT be turned on by Battery Saver.
+ * If set to true, the screen brightness WILL be turned on by Battery Saver.
*/
- private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled";
+ @VisibleForTesting
+ static final String KEY_ENABLE_BRIGHTNESS_ADJUSTMENT = "enable_brightness_adjustment";
/**
- * Disable turning on Data Saver when Battery Saver is turned on.
- * If set to false, Data Saver WILL be turned on when Battery Saver is turned on.
- * If set to true, Data Saver WILL NOT be turned on when Battery Saver is turned on.
+ * Turn on Data Saver when Battery Saver is turned on.
+ * If set to false, Data Saver WILL NOT be turned on when Battery Saver is turned on.
+ * If set to true, Data Saver WILL be turned on when Battery Saver is turned on.
*/
- private static final String KEY_ACTIVATE_DATASAVER_DISABLED = "datasaver_disabled";
+ @VisibleForTesting
+ static final String KEY_ENABLE_DATASAVER = "enable_datasaver";
/**
* {@code true} if the Policy should advertise to the rest of the system that battery saver
* is enabled. This advertising could cause other system components to change their
* behavior. This will not affect other policy flags and what they change.
*/
- private static final String KEY_ADVERTISE_IS_ENABLED = "advertise_is_enabled";
-
- private static final String KEY_LAUNCH_BOOST_DISABLED = "launch_boost_disabled";
- private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
- private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
- private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
- private static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
- private static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check";
- private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
- private static final String KEY_AOD_DISABLED = "aod_disabled";
+ @VisibleForTesting
+ static final String KEY_ADVERTISE_IS_ENABLED = "advertise_is_enabled";
+
+ @VisibleForTesting
+ static final String KEY_DISABLE_LAUNCH_BOOST = "disable_launch_boost";
+ @VisibleForTesting
+ static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
+ @VisibleForTesting
+ static final String KEY_DEFER_FULL_BACKUP = "defer_full_backup";
+ @VisibleForTesting
+ static final String KEY_DEFER_KEYVALUE_BACKUP = "defer_keyvalue_backup";
+ @VisibleForTesting
+ static final String KEY_FORCE_ALL_APPS_STANDBY = "force_all_apps_standby";
+ @VisibleForTesting
+ static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check";
+ @VisibleForTesting
+ static final String KEY_DISABLE_OPTIONAL_SENSORS = "disable_optional_sensors";
+ @VisibleForTesting
+ static final String KEY_DISABLE_AOD = "disable_aod";
// Go into deep Doze as soon as the screen turns off.
- private static final String KEY_QUICK_DOZE_ENABLED = "quick_doze_enabled";
- private static final String KEY_ENABLE_NIGHT_MODE = "enable_night_mode";
+ @VisibleForTesting
+ static final String KEY_ENABLE_QUICK_DOZE = "enable_quick_doze";
+ @VisibleForTesting
+ static final String KEY_ENABLE_NIGHT_MODE = "enable_night_mode";
private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
+ private static final String KEY_SUFFIX_ADAPTIVE = "_adaptive";
+
@VisibleForTesting
static final Policy OFF_POLICY = new Policy(
1f, /* adjustBrightnessFactor */
@@ -172,10 +196,7 @@ public class BatterySaverPolicy extends ContentObserver {
private String mDeviceSpecificSettingsSource; // For dump() only.
@GuardedBy("mLock")
- private String mAdaptiveSettings;
-
- @GuardedBy("mLock")
- private String mAdaptiveDeviceSpecificSettings;
+ private DeviceConfig.Properties mLastDeviceConfigProperties;
/**
* A short string describing which battery saver is now enabled, which we dump in the eventlog.
@@ -260,10 +281,6 @@ public class BatterySaverPolicy extends ContentObserver {
Settings.Global.BATTERY_SAVER_CONSTANTS), false, this);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS), false, this);
- mContentResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS), false, this);
- mContentResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS), false, this);
final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class);
@@ -276,6 +293,10 @@ public class BatterySaverPolicy extends ContentObserver {
mAutomotiveProjectionActive.initialize(
uiModeManager.getActiveProjectionTypes() != UiModeManager.PROJECTION_TYPE_NONE);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_SAVER,
+ mContext.getMainExecutor(), this);
+ mLastDeviceConfigProperties =
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_SAVER);
onChange(true, null);
}
@@ -304,7 +325,7 @@ public class BatterySaverPolicy extends ContentObserver {
/**
* Notifies listeners of a policy change on the handler thread only if the current policy level
- * is not {@link POLICY_LEVEL_OFF}.
+ * is not {@link #POLICY_LEVEL_OFF}.
*/
private void maybeNotifyListenersOfPolicyChange() {
final BatterySaverPolicyListener[] listeners;
@@ -329,6 +350,55 @@ public class BatterySaverPolicy extends ContentObserver {
refreshSettings();
}
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ // Need to get all of the flags atomically.
+ mLastDeviceConfigProperties =
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_BATTERY_SAVER);
+ Policy newAdaptivePolicy = null;
+ Policy newFullPolicy = null;
+
+ boolean changed = false;
+
+ synchronized (mLock) {
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
+ }
+ if (name.endsWith(KEY_SUFFIX_ADAPTIVE)) {
+ if (newAdaptivePolicy == null) {
+ newAdaptivePolicy = Policy.fromSettings("", "",
+ mLastDeviceConfigProperties, KEY_SUFFIX_ADAPTIVE,
+ DEFAULT_ADAPTIVE_POLICY);
+ }
+ } else if (newFullPolicy == null) {
+ newFullPolicy = Policy.fromSettings(mSettings, mDeviceSpecificSettings,
+ mLastDeviceConfigProperties, null, DEFAULT_FULL_POLICY);
+ }
+ }
+
+ if (newFullPolicy != null && !mFullPolicy.equals(newFullPolicy)) {
+ mFullPolicy = newFullPolicy;
+ changed |= (mPolicyLevel == POLICY_LEVEL_FULL);
+ }
+
+ if (newAdaptivePolicy != null && !mAdaptivePolicy.equals(newAdaptivePolicy)) {
+ mDefaultAdaptivePolicy = newAdaptivePolicy;
+ // This will override any config set by an external source. This should be fine
+ // for now.
+ // TODO(119261320): make sure it doesn't override what's set externally
+ mAdaptivePolicy = mDefaultAdaptivePolicy;
+ changed |= (mPolicyLevel == POLICY_LEVEL_ADAPTIVE);
+ }
+
+ updatePolicyDependenciesLocked();
+ }
+
+ if (changed) {
+ maybeNotifyListenersOfPolicyChange();
+ }
+ }
+
private void refreshSettings() {
synchronized (mLock) {
// Load the non-device-specific setting.
@@ -348,13 +418,7 @@ public class BatterySaverPolicy extends ContentObserver {
mDeviceSpecificSettingsSource = "(overlay)";
}
- final String adaptiveSetting =
- getGlobalSetting(Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS);
- final String adaptiveDeviceSpecificSetting = getGlobalSetting(
- Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS);
-
- if (!updateConstantsLocked(setting, deviceSpecificSetting,
- adaptiveSetting, adaptiveDeviceSpecificSetting)) {
+ if (!updateConstantsLocked(setting, deviceSpecificSetting)) {
// Nothing of note changed.
return;
}
@@ -365,47 +429,34 @@ public class BatterySaverPolicy extends ContentObserver {
@GuardedBy("mLock")
@VisibleForTesting
- void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
- updateConstantsLocked(setting, deviceSpecificSetting, "", "");
- }
-
/** @return true if the currently active policy changed. */
- private boolean updateConstantsLocked(String setting, String deviceSpecificSetting,
- String adaptiveSetting, String adaptiveDeviceSpecificSetting) {
+ boolean updateConstantsLocked(String setting, String deviceSpecificSetting) {
setting = TextUtils.emptyIfNull(setting);
deviceSpecificSetting = TextUtils.emptyIfNull(deviceSpecificSetting);
- adaptiveSetting = TextUtils.emptyIfNull(adaptiveSetting);
- adaptiveDeviceSpecificSetting = TextUtils.emptyIfNull(adaptiveDeviceSpecificSetting);
if (setting.equals(mSettings)
- && deviceSpecificSetting.equals(mDeviceSpecificSettings)
- && adaptiveSetting.equals(mAdaptiveSettings)
- && adaptiveDeviceSpecificSetting.equals(mAdaptiveDeviceSpecificSettings)) {
+ && deviceSpecificSetting.equals(mDeviceSpecificSettings)) {
return false;
}
mSettings = setting;
mDeviceSpecificSettings = deviceSpecificSetting;
- mAdaptiveSettings = adaptiveSetting;
- mAdaptiveDeviceSpecificSettings = adaptiveDeviceSpecificSetting;
if (DEBUG) {
Slog.i(TAG, "mSettings=" + mSettings);
Slog.i(TAG, "mDeviceSpecificSettings=" + mDeviceSpecificSettings);
- Slog.i(TAG, "mAdaptiveSettings=" + mAdaptiveSettings);
- Slog.i(TAG, "mAdaptiveDeviceSpecificSettings=" + mAdaptiveDeviceSpecificSettings);
}
boolean changed = false;
Policy newFullPolicy = Policy.fromSettings(setting, deviceSpecificSetting,
- DEFAULT_FULL_POLICY);
+ mLastDeviceConfigProperties, null, DEFAULT_FULL_POLICY);
if (mPolicyLevel == POLICY_LEVEL_FULL && !mFullPolicy.equals(newFullPolicy)) {
changed = true;
}
mFullPolicy = newFullPolicy;
- mDefaultAdaptivePolicy = Policy.fromSettings(adaptiveSetting, adaptiveDeviceSpecificSetting,
- DEFAULT_ADAPTIVE_POLICY);
+ mDefaultAdaptivePolicy = Policy.fromSettings("", "",
+ mLastDeviceConfigProperties, KEY_SUFFIX_ADAPTIVE, DEFAULT_ADAPTIVE_POLICY);
if (mPolicyLevel == POLICY_LEVEL_ADAPTIVE
&& !mAdaptivePolicy.equals(mDefaultAdaptivePolicy)) {
changed = true;
@@ -508,7 +559,7 @@ public class BatterySaverPolicy extends ContentObserver {
* {@code true} if full backup is deferred in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_FULLBACKUP_DEFERRED
+ * @see #KEY_DEFER_FULL_BACKUP
*/
public final boolean deferFullBackup;
@@ -516,7 +567,7 @@ public class BatterySaverPolicy extends ContentObserver {
* {@code true} if key value backup is deferred in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_KEYVALUE_DEFERRED
+ * @see #KEY_DEFER_KEYVALUE_BACKUP
*/
public final boolean deferKeyValueBackup;
@@ -524,7 +575,7 @@ public class BatterySaverPolicy extends ContentObserver {
* {@code true} if animation is disabled in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_ANIMATION_DISABLED
+ * @see #KEY_DISABLE_ANIMATION
*/
public final boolean disableAnimation;
@@ -548,7 +599,7 @@ public class BatterySaverPolicy extends ContentObserver {
* in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_SOUNDTRIGGER_DISABLED
+ * @see #KEY_DISABLE_SOUNDTRIGGER
*/
public final boolean disableSoundTrigger;
@@ -556,7 +607,7 @@ public class BatterySaverPolicy extends ContentObserver {
* {@code true} if vibration is disabled in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_VIBRATION_DISABLED
+ * @see #KEY_DISABLE_VIBRATION
*/
public final boolean disableVibration;
@@ -565,7 +616,7 @@ public class BatterySaverPolicy extends ContentObserver {
* mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_ADJUST_BRIGHTNESS_DISABLED
+ * @see #KEY_ENABLE_BRIGHTNESS_ADJUSTMENT
*/
public final boolean enableAdjustBrightness;
@@ -573,7 +624,7 @@ public class BatterySaverPolicy extends ContentObserver {
* {@code true} if data saver should be turned on in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_ACTIVATE_DATASAVER_DISABLED
+ * @see #KEY_ENABLE_DATASAVER
*/
public final boolean enableDataSaver;
@@ -581,7 +632,7 @@ public class BatterySaverPolicy extends ContentObserver {
* {@code true} if network policy firewall should be turned on in battery saver mode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_ACTIVATE_FIREWALL_DISABLED
+ * @see #KEY_ENABLE_FIREWALL
*/
public final boolean enableFirewall;
@@ -626,7 +677,7 @@ public class BatterySaverPolicy extends ContentObserver {
* previously called gpsMode.
*
* @see Settings.Global#BATTERY_SAVER_CONSTANTS
- * @see #KEY_GPS_MODE
+ * @see #KEY_LOCATION_MODE
*/
public final int locationMode;
@@ -744,13 +795,17 @@ public class BatterySaverPolicy extends ContentObserver {
);
}
- static Policy fromSettings(String settings, String deviceSpecificSettings) {
- return fromSettings(settings, deviceSpecificSettings, OFF_POLICY);
+ @VisibleForTesting
+ static Policy fromSettings(String settings, String deviceSpecificSettings,
+ DeviceConfig.Properties properties, String configSuffix) {
+ return fromSettings(settings, deviceSpecificSettings, properties, configSuffix,
+ OFF_POLICY);
}
- static Policy fromSettings(String settings, String deviceSpecificSettings,
- Policy defaultPolicy) {
+ private static Policy fromSettings(String settings, String deviceSpecificSettings,
+ DeviceConfig.Properties properties, String configSuffix, Policy defaultPolicy) {
final KeyValueListParser parser = new KeyValueListParser(',');
+ configSuffix = TextUtils.emptyIfNull(configSuffix);
// Device-specific parameters.
try {
@@ -770,40 +825,63 @@ public class BatterySaverPolicy extends ContentObserver {
Slog.wtf(TAG, "Bad battery saver constants: " + settings);
}
- float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
- defaultPolicy.adjustBrightnessFactor);
- boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
- defaultPolicy.advertiseIsEnabled);
- boolean deferFullBackup = parser.getBoolean(KEY_FULLBACKUP_DEFERRED,
- defaultPolicy.deferFullBackup);
- boolean deferKeyValueBackup = parser.getBoolean(KEY_KEYVALUE_DEFERRED,
- defaultPolicy.deferKeyValueBackup);
- boolean disableAnimation = parser.getBoolean(KEY_ANIMATION_DISABLED,
- defaultPolicy.disableAnimation);
- boolean disableAod = parser.getBoolean(KEY_AOD_DISABLED, defaultPolicy.disableAod);
- boolean disableLaunchBoost = parser.getBoolean(KEY_LAUNCH_BOOST_DISABLED,
- defaultPolicy.disableLaunchBoost);
- boolean disableOptionalSensors = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED,
- defaultPolicy.disableOptionalSensors);
- boolean disableSoundTrigger = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED,
- defaultPolicy.disableSoundTrigger);
- boolean disableVibrationConfig = parser.getBoolean(KEY_VIBRATION_DISABLED,
- defaultPolicy.disableVibration);
- boolean enableAdjustBrightness = !parser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED,
- !defaultPolicy.enableAdjustBrightness);
- boolean enableDataSaver = !parser.getBoolean(KEY_ACTIVATE_DATASAVER_DISABLED,
- !defaultPolicy.enableDataSaver);
- boolean enableFirewall = !parser.getBoolean(KEY_ACTIVATE_FIREWALL_DISABLED,
- !defaultPolicy.enableFirewall);
- boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE,
- defaultPolicy.enableNightMode);
- boolean enableQuickDoze = parser.getBoolean(KEY_QUICK_DOZE_ENABLED,
- defaultPolicy.enableQuickDoze);
- boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
- defaultPolicy.forceAllAppsStandby);
- boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
- defaultPolicy.forceBackgroundCheck);
- int locationMode = parser.getInt(KEY_GPS_MODE, defaultPolicy.locationMode);
+ // The Settings value overrides everything, since that will be set by the user.
+ // The DeviceConfig value takes second place, with the default as the last choice.
+ final float adjustBrightnessFactor = parser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR,
+ properties.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR + configSuffix,
+ defaultPolicy.adjustBrightnessFactor));
+ final boolean advertiseIsEnabled = parser.getBoolean(KEY_ADVERTISE_IS_ENABLED,
+ properties.getBoolean(KEY_ADVERTISE_IS_ENABLED + configSuffix,
+ defaultPolicy.advertiseIsEnabled));
+ final boolean deferFullBackup = parser.getBoolean(KEY_DEFER_FULL_BACKUP,
+ properties.getBoolean(KEY_DEFER_FULL_BACKUP + configSuffix,
+ defaultPolicy.deferFullBackup));
+ final boolean deferKeyValueBackup = parser.getBoolean(KEY_DEFER_KEYVALUE_BACKUP,
+ properties.getBoolean(KEY_DEFER_KEYVALUE_BACKUP + configSuffix,
+ defaultPolicy.deferKeyValueBackup));
+ final boolean disableAnimation = parser.getBoolean(KEY_DISABLE_ANIMATION,
+ properties.getBoolean(KEY_DISABLE_ANIMATION + configSuffix,
+ defaultPolicy.disableAnimation));
+ final boolean disableAod = parser.getBoolean(KEY_DISABLE_AOD,
+ properties.getBoolean(KEY_DISABLE_AOD + configSuffix,
+ defaultPolicy.disableAod));
+ final boolean disableLaunchBoost = parser.getBoolean(KEY_DISABLE_LAUNCH_BOOST,
+ properties.getBoolean(KEY_DISABLE_LAUNCH_BOOST + configSuffix,
+ defaultPolicy.disableLaunchBoost));
+ final boolean disableOptionalSensors = parser.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS,
+ properties.getBoolean(KEY_DISABLE_OPTIONAL_SENSORS + configSuffix,
+ defaultPolicy.disableOptionalSensors));
+ final boolean disableSoundTrigger = parser.getBoolean(KEY_DISABLE_SOUNDTRIGGER,
+ properties.getBoolean(KEY_DISABLE_SOUNDTRIGGER + configSuffix,
+ defaultPolicy.disableSoundTrigger));
+ final boolean disableVibrationConfig = parser.getBoolean(KEY_DISABLE_VIBRATION,
+ properties.getBoolean(KEY_DISABLE_VIBRATION + configSuffix,
+ defaultPolicy.disableVibration));
+ final boolean enableBrightnessAdjustment = parser.getBoolean(
+ KEY_ENABLE_BRIGHTNESS_ADJUSTMENT,
+ properties.getBoolean(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + configSuffix,
+ defaultPolicy.enableAdjustBrightness));
+ final boolean enableDataSaver = parser.getBoolean(KEY_ENABLE_DATASAVER,
+ properties.getBoolean(KEY_ENABLE_DATASAVER + configSuffix,
+ defaultPolicy.enableDataSaver));
+ final boolean enableFirewall = parser.getBoolean(KEY_ENABLE_FIREWALL,
+ properties.getBoolean(KEY_ENABLE_FIREWALL + configSuffix,
+ defaultPolicy.enableFirewall));
+ final boolean enableNightMode = parser.getBoolean(KEY_ENABLE_NIGHT_MODE,
+ properties.getBoolean(KEY_ENABLE_NIGHT_MODE + configSuffix,
+ defaultPolicy.enableNightMode));
+ final boolean enableQuickDoze = parser.getBoolean(KEY_ENABLE_QUICK_DOZE,
+ properties.getBoolean(KEY_ENABLE_QUICK_DOZE + configSuffix,
+ defaultPolicy.enableQuickDoze));
+ final boolean forceAllAppsStandby = parser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY,
+ properties.getBoolean(KEY_FORCE_ALL_APPS_STANDBY + configSuffix,
+ defaultPolicy.forceAllAppsStandby));
+ final boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
+ properties.getBoolean(KEY_FORCE_BACKGROUND_CHECK + configSuffix,
+ defaultPolicy.forceBackgroundCheck));
+ final int locationMode = parser.getInt(KEY_LOCATION_MODE,
+ properties.getInt(KEY_LOCATION_MODE + configSuffix,
+ defaultPolicy.locationMode));
return new Policy(
adjustBrightnessFactor,
@@ -817,7 +895,7 @@ public class BatterySaverPolicy extends ContentObserver {
disableSoundTrigger,
/* disableVibration */
disableVibrationConfig,
- enableAdjustBrightness,
+ enableBrightnessAdjustment,
enableDataSaver,
enableFirewall,
enableNightMode,
@@ -1031,90 +1109,94 @@ public class BatterySaverPolicy extends ContentObserver {
}
public void dump(PrintWriter pw) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+
synchronized (mLock) {
- pw.println();
- mBatterySavingStats.dump(pw, "");
-
- pw.println();
- pw.println("Battery saver policy (*NOTE* they only apply when battery saver is ON):");
- pw.println(" Settings: " + Settings.Global.BATTERY_SAVER_CONSTANTS);
- pw.println(" value: " + mSettings);
- pw.println(" Settings: " + mDeviceSpecificSettingsSource);
- pw.println(" value: " + mDeviceSpecificSettings);
-
- pw.println(" Adaptive Settings: " + Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS);
- pw.println(" value: " + mAdaptiveSettings);
- pw.println(" Adaptive Device Specific Settings: "
- + Settings.Global.BATTERY_SAVER_ADAPTIVE_DEVICE_SPECIFIC_CONSTANTS);
- pw.println(" value: " + mAdaptiveDeviceSpecificSettings);
-
- pw.println(" mAccessibilityEnabled=" + mAccessibilityEnabled.get());
- pw.println(" mAutomotiveProjectionActive=" + mAutomotiveProjectionActive.get());
- pw.println(" mPolicyLevel=" + mPolicyLevel);
-
- dumpPolicyLocked(pw, " ", "full", mFullPolicy);
- dumpPolicyLocked(pw, " ", "default adaptive", mDefaultAdaptivePolicy);
- dumpPolicyLocked(pw, " ", "current adaptive", mAdaptivePolicy);
- dumpPolicyLocked(pw, " ", "effective", mEffectivePolicyRaw);
+ ipw.println();
+ mBatterySavingStats.dump(ipw);
+
+ ipw.println();
+ ipw.println("Battery saver policy (*NOTE* they only apply when battery saver is ON):");
+ ipw.increaseIndent();
+ ipw.println("Settings: " + Settings.Global.BATTERY_SAVER_CONSTANTS);
+ ipw.increaseIndent();
+ ipw.println("value: " + mSettings);
+ ipw.decreaseIndent();
+ ipw.println("Settings: " + mDeviceSpecificSettingsSource);
+ ipw.increaseIndent();
+ ipw.println("value: " + mDeviceSpecificSettings);
+ ipw.decreaseIndent();
+ ipw.println("DeviceConfig: " + DeviceConfig.NAMESPACE_BATTERY_SAVER);
+ ipw.increaseIndent();
+ final Set<String> keys = mLastDeviceConfigProperties.getKeyset();
+ if (keys.size() == 0) {
+ ipw.println("N/A");
+ } else {
+ for (final String key : keys) {
+ ipw.print(key);
+ ipw.print(": ");
+ ipw.println(mLastDeviceConfigProperties.getString(key, null));
+ }
+ }
+
+ ipw.println("mAccessibilityEnabled=" + mAccessibilityEnabled.get());
+ ipw.println("mAutomotiveProjectionActive=" + mAutomotiveProjectionActive.get());
+ ipw.println("mPolicyLevel=" + mPolicyLevel);
+
+ dumpPolicyLocked(ipw, "full", mFullPolicy);
+ dumpPolicyLocked(ipw, "default adaptive", mDefaultAdaptivePolicy);
+ dumpPolicyLocked(ipw, "current adaptive", mAdaptivePolicy);
+ dumpPolicyLocked(ipw, "effective", mEffectivePolicyRaw);
+
+ ipw.decreaseIndent();
}
}
- private void dumpPolicyLocked(PrintWriter pw, String indent, String label, Policy p) {
+ private void dumpPolicyLocked(IndentingPrintWriter pw, String label, Policy p) {
pw.println();
- pw.print(indent);
pw.println("Policy '" + label + "'");
- pw.print(indent);
- pw.println(" " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled);
- pw.print(indent);
- pw.println(" " + KEY_VIBRATION_DISABLED + "=" + p.disableVibration);
- pw.print(indent);
- pw.println(" " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation);
- pw.print(indent);
- pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + p.deferFullBackup);
- pw.print(indent);
- pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + p.deferKeyValueBackup);
- pw.print(indent);
- pw.println(" " + KEY_ACTIVATE_FIREWALL_DISABLED + "=" + !p.enableFirewall);
- pw.print(indent);
- pw.println(" " + KEY_ACTIVATE_DATASAVER_DISABLED + "=" + !p.enableDataSaver);
- pw.print(indent);
- pw.println(" " + KEY_LAUNCH_BOOST_DISABLED + "=" + p.disableLaunchBoost);
- pw.println(
- " " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + !p.enableAdjustBrightness);
- pw.print(indent);
- pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor);
- pw.print(indent);
- pw.println(" " + KEY_GPS_MODE + "=" + p.locationMode);
- pw.print(indent);
- pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby);
- pw.print(indent);
- pw.println(" " + KEY_FORCE_BACKGROUND_CHECK + "=" + p.forceBackgroundCheck);
- pw.println(
- " " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + p.disableOptionalSensors);
- pw.print(indent);
- pw.println(" " + KEY_AOD_DISABLED + "=" + p.disableAod);
- pw.print(indent);
- pw.println(" " + KEY_SOUNDTRIGGER_DISABLED + "=" + p.disableSoundTrigger);
- pw.print(indent);
- pw.println(" " + KEY_QUICK_DOZE_ENABLED + "=" + p.enableQuickDoze);
- pw.print(indent);
- pw.println(" " + KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode);
-
- pw.print(" Interactive File values:\n");
- dumpMap(pw, " ", p.filesForInteractive);
+ pw.increaseIndent();
+ pw.println(KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled);
+ pw.println(KEY_DISABLE_VIBRATION + "=" + p.disableVibration);
+ pw.println(KEY_DISABLE_ANIMATION + "=" + p.disableAnimation);
+ pw.println(KEY_DEFER_FULL_BACKUP + "=" + p.deferFullBackup);
+ pw.println(KEY_DEFER_KEYVALUE_BACKUP + "=" + p.deferKeyValueBackup);
+ pw.println(KEY_ENABLE_FIREWALL + "=" + p.enableFirewall);
+ pw.println(KEY_ENABLE_DATASAVER + "=" + p.enableDataSaver);
+ pw.println(KEY_DISABLE_LAUNCH_BOOST + "=" + p.disableLaunchBoost);
+ pw.println(KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + "=" + p.enableAdjustBrightness);
+ pw.println(KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + p.adjustBrightnessFactor);
+ pw.println(KEY_LOCATION_MODE + "=" + p.locationMode);
+ pw.println(KEY_FORCE_ALL_APPS_STANDBY + "=" + p.forceAllAppsStandby);
+ pw.println(KEY_FORCE_BACKGROUND_CHECK + "=" + p.forceBackgroundCheck);
+ pw.println(KEY_DISABLE_OPTIONAL_SENSORS + "=" + p.disableOptionalSensors);
+ pw.println(KEY_DISABLE_AOD + "=" + p.disableAod);
+ pw.println(KEY_DISABLE_SOUNDTRIGGER + "=" + p.disableSoundTrigger);
+ pw.println(KEY_ENABLE_QUICK_DOZE + "=" + p.enableQuickDoze);
+ pw.println(KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode);
+
+ pw.println("Interactive File values:");
+ pw.increaseIndent();
+ dumpMap(pw, p.filesForInteractive);
+ pw.decreaseIndent();
pw.println();
- pw.print(" Noninteractive File values:\n");
- dumpMap(pw, " ", p.filesForNoninteractive);
+ pw.println("Noninteractive File values:");
+ pw.increaseIndent();
+ dumpMap(pw, p.filesForNoninteractive);
+ pw.decreaseIndent();
+
+ // Decrease from indent right after "Policy" line
+ pw.decreaseIndent();
}
- private void dumpMap(PrintWriter pw, String prefix, ArrayMap<String, String> map) {
+ private void dumpMap(PrintWriter pw, ArrayMap<String, String> map) {
if (map == null) {
+ pw.println("N/A");
return;
}
final int size = map.size();
for (int i = 0; i < size; i++) {
- pw.print(prefix);
pw.print(map.keyAt(i));
pw.print(": '");
pw.print(map.valueAt(i));
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index af14d84d12b8..21500f649099 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -34,6 +34,7 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -888,70 +889,75 @@ public class BatterySaverStateMachine {
}
public void dump(PrintWriter pw) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+
+ ipw.println();
+ ipw.println("Battery saver state machine:");
+ ipw.increaseIndent();
synchronized (mLock) {
- pw.println();
- pw.println("Battery saver state machine:");
-
- pw.print(" Enabled=");
- pw.println(mBatterySaverController.isEnabled());
- pw.print(" full=");
- pw.println(mBatterySaverController.isFullEnabled());
- pw.print(" adaptive=");
- pw.print(mBatterySaverController.isAdaptiveEnabled());
+ ipw.print("Enabled=");
+ ipw.println(mBatterySaverController.isEnabled());
+ ipw.increaseIndent();
+ ipw.print("full=");
+ ipw.println(mBatterySaverController.isFullEnabled());
+ ipw.print("adaptive=");
+ ipw.print(mBatterySaverController.isAdaptiveEnabled());
if (mBatterySaverController.isAdaptiveEnabled()) {
- pw.print(" (advertise=");
- pw.print(
+ ipw.print(" (advertise=");
+ ipw.print(
mBatterySaverController.getBatterySaverPolicy().shouldAdvertiseIsEnabled());
- pw.print(")");
+ ipw.print(")");
}
- pw.println();
- pw.print(" mState=");
- pw.println(mState);
-
- pw.print(" mLastChangedIntReason=");
- pw.println(mLastChangedIntReason);
- pw.print(" mLastChangedStrReason=");
- pw.println(mLastChangedStrReason);
-
- pw.print(" mBootCompleted=");
- pw.println(mBootCompleted);
- pw.print(" mSettingsLoaded=");
- pw.println(mSettingsLoaded);
- pw.print(" mBatteryStatusSet=");
- pw.println(mBatteryStatusSet);
-
- pw.print(" mIsPowered=");
- pw.println(mIsPowered);
- pw.print(" mBatteryLevel=");
- pw.println(mBatteryLevel);
- pw.print(" mIsBatteryLevelLow=");
- pw.println(mIsBatteryLevelLow);
-
- pw.print(" mSettingAutomaticBatterySaver=");
- pw.println(mSettingAutomaticBatterySaver);
- pw.print(" mSettingBatterySaverEnabled=");
- pw.println(mSettingBatterySaverEnabled);
- pw.print(" mSettingBatterySaverEnabledSticky=");
- pw.println(mSettingBatterySaverEnabledSticky);
- pw.print(" mSettingBatterySaverStickyAutoDisableEnabled=");
- pw.println(mSettingBatterySaverStickyAutoDisableEnabled);
- pw.print(" mSettingBatterySaverStickyAutoDisableThreshold=");
- pw.println(mSettingBatterySaverStickyAutoDisableThreshold);
- pw.print(" mSettingBatterySaverTriggerThreshold=");
- pw.println(mSettingBatterySaverTriggerThreshold);
- pw.print(" mBatterySaverStickyBehaviourDisabled=");
- pw.println(mBatterySaverStickyBehaviourDisabled);
-
- pw.print(" mDynamicPowerSavingsDefaultDisableThreshold=");
- pw.println(mDynamicPowerSavingsDefaultDisableThreshold);
- pw.print(" mDynamicPowerSavingsDisableThreshold=");
- pw.println(mDynamicPowerSavingsDisableThreshold);
- pw.print(" mDynamicPowerSavingsEnableBatterySaver=");
- pw.println(mDynamicPowerSavingsEnableBatterySaver);
-
- pw.print(" mLastAdaptiveBatterySaverChangedExternallyElapsed=");
- pw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed);
+ ipw.decreaseIndent();
+ ipw.println();
+ ipw.print("mState=");
+ ipw.println(mState);
+
+ ipw.print("mLastChangedIntReason=");
+ ipw.println(mLastChangedIntReason);
+ ipw.print("mLastChangedStrReason=");
+ ipw.println(mLastChangedStrReason);
+
+ ipw.print("mBootCompleted=");
+ ipw.println(mBootCompleted);
+ ipw.print("mSettingsLoaded=");
+ ipw.println(mSettingsLoaded);
+ ipw.print("mBatteryStatusSet=");
+ ipw.println(mBatteryStatusSet);
+
+ ipw.print("mIsPowered=");
+ ipw.println(mIsPowered);
+ ipw.print("mBatteryLevel=");
+ ipw.println(mBatteryLevel);
+ ipw.print("mIsBatteryLevelLow=");
+ ipw.println(mIsBatteryLevelLow);
+
+ ipw.print("mSettingAutomaticBatterySaver=");
+ ipw.println(mSettingAutomaticBatterySaver);
+ ipw.print("mSettingBatterySaverEnabled=");
+ ipw.println(mSettingBatterySaverEnabled);
+ ipw.print("mSettingBatterySaverEnabledSticky=");
+ ipw.println(mSettingBatterySaverEnabledSticky);
+ ipw.print("mSettingBatterySaverStickyAutoDisableEnabled=");
+ ipw.println(mSettingBatterySaverStickyAutoDisableEnabled);
+ ipw.print("mSettingBatterySaverStickyAutoDisableThreshold=");
+ ipw.println(mSettingBatterySaverStickyAutoDisableThreshold);
+ ipw.print("mSettingBatterySaverTriggerThreshold=");
+ ipw.println(mSettingBatterySaverTriggerThreshold);
+ ipw.print("mBatterySaverStickyBehaviourDisabled=");
+ ipw.println(mBatterySaverStickyBehaviourDisabled);
+
+ ipw.print("mDynamicPowerSavingsDefaultDisableThreshold=");
+ ipw.println(mDynamicPowerSavingsDefaultDisableThreshold);
+ ipw.print("mDynamicPowerSavingsDisableThreshold=");
+ ipw.println(mDynamicPowerSavingsDisableThreshold);
+ ipw.print("mDynamicPowerSavingsEnableBatterySaver=");
+ ipw.println(mDynamicPowerSavingsEnableBatterySaver);
+
+ ipw.print("mLastAdaptiveBatterySaverChangedExternallyElapsed=");
+ ipw.println(mLastAdaptiveBatterySaverChangedExternallyElapsed);
}
+ ipw.decreaseIndent();
}
public void dumpProto(ProtoOutputStream proto, long tag) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 05695d919910..a7be2677cf23 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -17,6 +17,7 @@ package com.android.server.power.batterysaver;
import android.os.BatteryManagerInternal;
import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -26,7 +27,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
-import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -391,18 +391,15 @@ public class BatterySavingStats {
stat.endTime = 0;
}
- public void dump(PrintWriter pw, String indent) {
- synchronized (mLock) {
- pw.print(indent);
- pw.println("Battery saving stats:");
-
- indent = indent + " ";
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Battery saving stats:");
+ pw.increaseIndent();
+ synchronized (mLock) {
final long now = System.currentTimeMillis();
final long nowElapsed = injectCurrentTime();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- pw.print(indent);
pw.print("Battery Saver is currently: ");
switch (BatterySaverState.fromIndex(mCurrentState)) {
case BatterySaverState.OFF:
@@ -416,9 +413,8 @@ public class BatterySavingStats {
break;
}
+ pw.increaseIndent();
if (mLastBatterySaverEnabledTime > 0) {
- pw.print(indent);
- pw.print(" ");
pw.print("Last ON time: ");
pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverEnabledTime)));
pw.print(" ");
@@ -427,8 +423,6 @@ public class BatterySavingStats {
}
if (mLastBatterySaverDisabledTime > 0) {
- pw.print(indent);
- pw.print(" ");
pw.print("Last OFF time: ");
pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverDisabledTime)));
pw.print(" ");
@@ -436,14 +430,10 @@ public class BatterySavingStats {
pw.println();
}
- pw.print(indent);
- pw.print(" ");
pw.print("Times full enabled: ");
pw.println(mBatterySaverEnabledCount);
if (mLastAdaptiveBatterySaverEnabledTime > 0) {
- pw.print(indent);
- pw.print(" ");
pw.print("Last ADAPTIVE ON time: ");
pw.print(sdf.format(
new Date(now - nowElapsed + mLastAdaptiveBatterySaverEnabledTime)));
@@ -452,8 +442,6 @@ public class BatterySavingStats {
pw.println();
}
if (mLastAdaptiveBatterySaverDisabledTime > 0) {
- pw.print(indent);
- pw.print(" ");
pw.print("Last ADAPTIVE OFF time: ");
pw.print(sdf.format(
new Date(now - nowElapsed + mLastAdaptiveBatterySaverDisabledTime)));
@@ -461,39 +449,36 @@ public class BatterySavingStats {
TimeUtils.formatDuration(mLastAdaptiveBatterySaverDisabledTime, nowElapsed, pw);
pw.println();
}
- pw.print(indent);
- pw.print(" ");
pw.print("Times adaptive enabled: ");
pw.println(mAdaptiveBatterySaverEnabledCount);
+ pw.decreaseIndent();
pw.println();
- pw.print(indent);
pw.println("Drain stats:");
- pw.print(indent);
pw.println(" Battery saver OFF ON");
- dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
+ dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.NOT_DOZING, "NonDoze");
- dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
+ dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr",
DozeState.NOT_DOZING, " ");
- dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
+ dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.DEEP, "Deep ");
- dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
+ dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr",
DozeState.DEEP, " ");
- dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
+ dumpLineLocked(pw, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.LIGHT, "Light ");
- dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
+ dumpLineLocked(pw, InteractiveState.INTERACTIVE, " Intr",
DozeState.LIGHT, " ");
}
+ pw.decreaseIndent();
}
- private void dumpLineLocked(PrintWriter pw, String indent,
+ private void dumpLineLocked(IndentingPrintWriter pw,
int interactiveState, String interactiveLabel,
int dozeState, String dozeLabel) {
- pw.print(indent);
pw.print(dozeLabel);
pw.print(" ");
pw.print(interactiveLabel);
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index ab6ada2f85f7..eb15c808f512 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -71,7 +71,6 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
@@ -83,7 +82,6 @@ import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
/**
* Service for role management.
@@ -162,12 +160,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
LocalServices.addService(RoleManagerInternal.class, new Internal());
- PermissionManagerServiceInternal permissionManagerInternal =
- LocalServices.getService(PermissionManagerServiceInternal.class);
- permissionManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider());
- permissionManagerInternal.setDefaultDialerProvider(new DefaultDialerProvider());
- permissionManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider());
-
registerUserRemovedReceiver();
}
@@ -657,12 +649,78 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
resultReceiver);
}
+ @Nullable
+ @Override
+ public String getBrowserRoleHolder(@UserIdInt int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (UserHandle.getUserId(callingUid) != userId) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ }
+ final PackageManagerInternal packageManager = LocalServices.getService(
+ PackageManagerInternal.class);
+ if (packageManager.getInstantAppPackageName(callingUid) != null) {
+ return null;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_BROWSER,
+ userId));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) {
+ final Context context = getContext();
+ context.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+ if (UserHandle.getCallingUserId() != userId) {
+ context.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+ }
+
+ if (!mUserManagerInternal.exists(userId)) {
+ return false;
+ }
+
+ final AndroidFuture<Void> future = new AndroidFuture<>();
+ final RemoteCallback callback = new RemoteCallback(result -> {
+ boolean successful = result != null;
+ if (successful) {
+ future.complete(null);
+ } else {
+ future.completeExceptionally(new RuntimeException());
+ }
+ });
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (packageName != null) {
+ addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, userId, callback);
+ } else {
+ clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, userId, callback);
+ }
+ try {
+ future.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.e(LOG_TAG, "Exception while setting default browser: " + packageName, e);
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ return true;
+ }
+
@Override
- public String getDefaultSmsPackage(int userId) {
+ public String getSmsRoleHolder(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
- return CollectionUtils.firstOrNull(
- getRoleHoldersAsUser(RoleManager.ROLE_SMS, userId));
+ return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_SMS,
+ userId));
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -718,100 +776,4 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
return getOrCreateUserState(userId).getRolesAndHolders();
}
}
-
- private class DefaultBrowserProvider implements
- PermissionManagerServiceInternal.DefaultBrowserProvider {
-
- @Nullable
- @Override
- public String getDefaultBrowser(@UserIdInt int userId) {
- return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders(
- RoleManager.ROLE_BROWSER));
- }
-
- @Override
- public boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId) {
- AndroidFuture<Void> future = new AndroidFuture<>();
- RemoteCallback callback = new RemoteCallback(result -> {
- boolean successful = result != null;
- if (successful) {
- future.complete(null);
- } else {
- future.completeExceptionally(new RuntimeException());
- }
- });
- if (packageName != null) {
- getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER,
- packageName, 0, callback);
- } else {
- getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0,
- callback);
- }
- try {
- future.get(5, TimeUnit.SECONDS);
- return true;
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Slog.e(LOG_TAG, "Exception while setting default browser: " + packageName, e);
- return false;
- }
- }
-
- @Override
- public void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId) {
- RemoteCallback callback = new RemoteCallback(result -> {
- boolean successful = result != null;
- if (!successful) {
- Slog.e(LOG_TAG, "Failed to set default browser: " + packageName);
- }
- });
- if (packageName != null) {
- getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER,
- packageName, 0, callback);
- } else {
- getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0,
- callback);
- }
- }
- }
-
- private class DefaultDialerProvider implements
- PermissionManagerServiceInternal.DefaultDialerProvider {
-
- @Nullable
- @Override
- public String getDefaultDialer(@UserIdInt int userId) {
- return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders(
- RoleManager.ROLE_DIALER));
- }
- }
-
- private class DefaultHomeProvider implements
- PermissionManagerServiceInternal.DefaultHomeProvider {
-
- @Nullable
- @Override
- public String getDefaultHome(@UserIdInt int userId) {
- return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders(
- RoleManager.ROLE_HOME));
- }
-
- @Override
- public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId,
- @NonNull Consumer<Boolean> callback) {
- RemoteCallback remoteCallback = new RemoteCallback(result -> {
- boolean successful = result != null;
- if (!successful) {
- Slog.e(LOG_TAG, "Failed to set default home: " + packageName);
- }
- callback.accept(successful);
- });
- if (packageName != null) {
- getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_HOME,
- packageName, 0, remoteCallback);
- } else {
- getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0,
- remoteCallback);
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 63ed416f2859..d9b67024018b 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -581,9 +581,18 @@ class Rollback {
ParcelFileDescriptor.MODE_READ_ONLY)) {
final long token = Binder.clearCallingIdentity();
try {
- session.write(packageCodePath.getName(), 0,
- packageCodePath.length(),
- fd);
+ boolean fallbackToCopy = false;
+ try {
+ // Populate apk/apex files using hard links to avoid copy
+ session.stageViaHardLink(packageCodePath.getAbsolutePath());
+ } catch (Exception ignore) {
+ fallbackToCopy = true;
+ }
+ if (fallbackToCopy) {
+ session.write(packageCodePath.getName(), 0,
+ packageCodePath.length(),
+ fd);
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index ee9694f90216..ee0e5ba916b9 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -256,11 +256,6 @@ public class SliceManagerService extends ISliceManager.Stub {
}
}
}
- // Fallback to allowing uri permissions through.
- if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- == PERMISSION_GRANTED) {
- return PackageManager.PERMISSION_GRANTED;
- }
return PackageManager.PERMISSION_DENIED;
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index d3b1ac6e6096..cf20cf4c0c9f 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -502,6 +502,8 @@ public class StatsPullAtomService extends SystemService {
synchronized (mProcessSystemIonHeapSizeLock) {
return pullProcessSystemIonHeapSizeLocked(atomTag, data);
}
+ case FrameworkStatsLog.SYSTEM_MEMORY:
+ return pullSystemMemory(atomTag, data);
case FrameworkStatsLog.TEMPERATURE:
synchronized (mTemperatureLock) {
return pullTemperatureLocked(atomTag, data);
@@ -796,6 +798,7 @@ public class StatsPullAtomService extends SystemService {
registerSystemIonHeapSize();
registerIonHeapSize();
registerProcessSystemIonHeapSize();
+ registerSystemMemory();
registerTemperature();
registerCoolingDevice();
registerBinderCallsStats();
@@ -1913,6 +1916,30 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
+ private void registerSystemMemory() {
+ int tagId = FrameworkStatsLog.SYSTEM_MEMORY;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
+ int pullSystemMemory(int atomTag, List<StatsEvent> pulledData) {
+ SystemMemoryUtil.Metrics metrics = SystemMemoryUtil.getMetrics();
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ metrics.unreclaimableSlabKb,
+ metrics.vmallocUsedKb,
+ metrics.pageTablesKb,
+ metrics.kernelStackKb,
+ metrics.totalIonKb,
+ metrics.unaccountedKb));
+ return StatsManager.PULL_SUCCESS;
+ }
+
private void registerTemperature() {
int tagId = FrameworkStatsLog.TEMPERATURE;
mStatsManager.setPullAtomCallback(
diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
new file mode 100644
index 000000000000..99fc7c11c5b3
--- /dev/null
+++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 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.stats.pull;
+
+import android.os.Debug;
+
+/**
+ * Snapshots system-wide memory stats and computes unaccounted memory.
+ * Thread-safe.
+ */
+final class SystemMemoryUtil {
+ private SystemMemoryUtil() {}
+
+ static Metrics getMetrics() {
+ int totalIonKb = (int) Debug.getIonHeapsSizeKb();
+
+ long[] mInfos = new long[Debug.MEMINFO_COUNT];
+ Debug.getMemInfo(mInfos);
+
+ long kReclaimableKb = mInfos[Debug.MEMINFO_KRECLAIMABLE];
+ // Note: MEMINFO_KRECLAIMABLE includes MEMINFO_SLAB_RECLAIMABLE and ION pools.
+ // Fall back to using MEMINFO_SLAB_RECLAIMABLE in case of older kernels that do
+ // not include KReclaimable meminfo field.
+ if (kReclaimableKb == 0) {
+ kReclaimableKb = mInfos[Debug.MEMINFO_SLAB_RECLAIMABLE];
+ }
+
+ long accountedKb = mInfos[Debug.MEMINFO_FREE]
+ + mInfos[Debug.MEMINFO_ZRAM_TOTAL]
+ + mInfos[Debug.MEMINFO_BUFFERS]
+ + mInfos[Debug.MEMINFO_ACTIVE]
+ + mInfos[Debug.MEMINFO_INACTIVE]
+ + mInfos[Debug.MEMINFO_UNEVICTABLE]
+ + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]
+ + kReclaimableKb
+ + mInfos[Debug.MEMINFO_VM_ALLOC_USED]
+ + mInfos[Debug.MEMINFO_PAGE_TABLES]
+ + Math.max(totalIonKb, 0);
+
+ if (!Debug.isVmapStack()) {
+ // See b/146088882
+ accountedKb += mInfos[Debug.MEMINFO_KERNEL_STACK];
+ }
+
+ Metrics result = new Metrics();
+ result.unreclaimableSlabKb = (int) mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE];
+ result.vmallocUsedKb = (int) mInfos[Debug.MEMINFO_VM_ALLOC_USED];
+ result.pageTablesKb = (int) mInfos[Debug.MEMINFO_PAGE_TABLES];
+ result.kernelStackKb = (int) mInfos[Debug.MEMINFO_KERNEL_STACK];
+ result.totalIonKb = totalIonKb;
+ result.unaccountedKb = (int) (mInfos[Debug.MEMINFO_TOTAL] - accountedKb);
+ return result;
+ }
+
+ static final class Metrics {
+ public int unreclaimableSlabKb;
+ public int vmallocUsedKb;
+ public int pageTablesKb;
+ public int kernelStackKb;
+ public int totalIonKb;
+ public int unaccountedKb;
+ }
+}
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 52ad893a9ace..f0c96e18930a 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -42,7 +42,7 @@ import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerService;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
/**
* Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
@@ -65,8 +65,8 @@ public class TelecomLoaderService extends SystemService {
ServiceManager.addService(Context.TELECOM_SERVICE, telecomService.asBinder());
synchronized (mLock) {
- final PermissionManagerServiceInternal permissionManager =
- LocalServices.getService(PermissionManagerServiceInternal.class);
+ final LegacyPermissionManagerInternal permissionManager =
+ LocalServices.getService(LegacyPermissionManagerInternal.class);
if (mDefaultSimCallManagerRequests != null) {
if (mDefaultSimCallManagerRequests != null) {
TelecomManager telecomManager =
@@ -165,8 +165,8 @@ public class TelecomLoaderService extends SystemService {
private void registerDefaultAppProviders() {
- final PermissionManagerServiceInternal permissionManager =
- LocalServices.getService(PermissionManagerServiceInternal.class);
+ final LegacyPermissionManagerInternal permissionManager =
+ LocalServices.getService(LegacyPermissionManagerInternal.class);
// Set a callback for the permission grant policy to query the default sms app.
permissionManager.setSmsAppPackagesProvider(userId -> {
@@ -244,15 +244,16 @@ public class TelecomLoaderService extends SystemService {
}
private void updateSimCallManagerPermissions(int userId) {
- final PermissionManagerServiceInternal permissionManager =
- LocalServices.getService(PermissionManagerServiceInternal.class);
+ final LegacyPermissionManagerInternal permissionManager =
+ LocalServices.getService(LegacyPermissionManagerInternal.class);
TelecomManager telecomManager =
(TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
if (phoneAccount != null) {
Slog.i(TAG, "updating sim call manager permissions for userId:" + userId);
String packageName = phoneAccount.getComponentName().getPackageName();
- permissionManager.grantDefaultPermissionsToDefaultSimCallManager(packageName, userId);
+ permissionManager.grantDefaultPermissionsToDefaultSimCallManager(packageName,
+ userId);
}
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
index 118899add968..1867ee207958 100644
--- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
+++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java
@@ -61,7 +61,7 @@ import java.util.StringTokenizer;
*/
public final class GeolocationTimeZoneSuggestion {
- @NonNull private final List<String> mZoneIds;
+ @Nullable private final List<String> mZoneIds;
@Nullable private ArrayList<String> mDebugInfo;
public GeolocationTimeZoneSuggestion(@Nullable List<String> zoneIds) {
diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java
index d3c9b3bbe7f5..72556a75a4b5 100644
--- a/services/core/java/com/android/server/tv/PersistentDataStore.java
+++ b/services/core/java/com/android/server/tv/PersistentDataStore.java
@@ -30,23 +30,17 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -246,12 +240,7 @@ final class PersistentDataStore {
if (parser.getName().equals(TAG_BLOCKED_RATINGS)) {
loadBlockedRatingsFromXml(parser);
} else if (parser.getName().equals(TAG_PARENTAL_CONTROLS)) {
- String enabled = parser.getAttributeValue(null, ATTR_ENABLED);
- if (TextUtils.isEmpty(enabled)) {
- throw new XmlPullParserException(
- "Missing " + ATTR_ENABLED + " attribute on " + TAG_PARENTAL_CONTROLS);
- }
- mParentalControlsEnabled = Boolean.parseBoolean(enabled);
+ mParentalControlsEnabled = parser.getAttributeBoolean(null, ATTR_ENABLED);
}
}
}
diff --git a/services/core/java/com/android/server/tv/TEST_MAPPING b/services/core/java/com/android/server/tv/TEST_MAPPING
new file mode 100644
index 000000000000..f718f909fecb
--- /dev/null
+++ b/services/core/java/com/android/server/tv/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "cts/tests/tests/tv"
+ }
+ ]
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 510893b2940b..1754e593914d 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -84,6 +84,7 @@ import android.view.InputChannel;
import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
@@ -768,6 +769,7 @@ public final class TvInputManagerService extends SystemService {
SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
if (sessionState == null) {
+ Slog.e(TAG, "sessionState null, no more remove session action!");
return;
}
@@ -1441,8 +1443,8 @@ public final class TvInputManagerService extends SystemService {
if (sessionState != null) {
int state = surface == null
?
- FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED
- : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED;
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED
+ : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED;
logTuneStateChanged(state, sessionState,
TvInputManagerService.getTvInputState(sessionState, userState));
}
@@ -2525,8 +2527,16 @@ public final class TvInputManagerService extends SystemService {
ClientState clientState = userState.clientStateMap.get(clientToken);
if (clientState != null) {
while (clientState.sessionTokens.size() > 0) {
+ IBinder sessionToken = clientState.sessionTokens.get(0);
releaseSessionLocked(
- clientState.sessionTokens.get(0), Process.SYSTEM_UID, userId);
+ sessionToken, Process.SYSTEM_UID, userId);
+ // the releaseSessionLocked function may return before the sessionToken
+ // is removed if the related sessionState is null. So need to check again
+ // to avoid death curculation.
+ if (clientState.sessionTokens.contains(sessionToken)) {
+ Slog.d(TAG, "remove sessionToken " + sessionToken + " for " + clientToken);
+ clientState.sessionTokens.remove(sessionToken);
+ }
}
}
clientToken = null;
@@ -2963,15 +2973,7 @@ public final class TvInputManagerService extends SystemService {
getUserStateLocked(mCurrentUserId));
try {
mSessionState.client.onVideoUnavailable(reason, mSessionState.seq);
- int loggedReason = reason + FrameworkStatsLog
- .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
- if (loggedReason < FrameworkStatsLog
- .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN
- || loggedReason > FrameworkStatsLog
- .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) {
- loggedReason = FrameworkStatsLog
- .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
- }
+ int loggedReason = getVideoUnavailableReasonForStatsd(reason);
logTuneStateChanged(loggedReason, mSessionState, tvInputState);
} catch (RemoteException e) {
Slog.e(TAG, "error in onVideoUnavailable", e);
@@ -3158,6 +3160,21 @@ public final class TvInputManagerService extends SystemService {
}
}
+ @VisibleForTesting
+ static int getVideoUnavailableReasonForStatsd(
+ @TvInputManager.VideoUnavailableReason int reason) {
+ int loggedReason = reason + FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+ if (loggedReason < FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN
+ || loggedReason > FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) {
+ loggedReason = FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+ }
+ return loggedReason;
+ }
+
private UserState getUserStateLocked(int userId) {
return mUserStates.get(userId);
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
index beb11ed4ea0c..7f49eead19fa 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
@@ -192,8 +192,9 @@ public class UseCasePriorityHints {
}
}
- private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) {
- return Integer.valueOf(parser.getAttributeValue(null, attributeName));
+ private int readAttributeToInt(String attributeName, TypedXmlPullParser parser)
+ throws XmlPullParserException {
+ return parser.getAttributeInt(null, attributeName);
}
private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) {
diff --git a/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java
new file mode 100644
index 000000000000..fdbe4b425d39
--- /dev/null
+++ b/services/core/java/com/android/server/utils/quota/MultiRateLimiter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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.utils.quota;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Can be used to rate limit events per app based on multiple rates at the same time. For example,
+ * it can limit an event to happen only:
+ *
+ * <li>5 times in 20 seconds</li>
+ * and
+ * <li>6 times in 40 seconds</li>
+ * and
+ * <li>10 times in 1 hour</li>
+ *
+ * <p><br>
+ * All listed rates apply at the same time, and the UPTC will be out of quota if it doesn't satisfy
+ * all the given rates. The underlying mechanism used is
+ * {@link com.android.server.utils.quota.CountQuotaTracker}, so all its conditions apply, as well
+ * as an additional constraint: all the user-package-tag combinations (UPTC) are considered to be in
+ * the same {@link com.android.server.utils.quota.Category}.
+ * </p>
+ *
+ * @hide
+ */
+public class MultiRateLimiter {
+
+ private static final CountQuotaTracker[] EMPTY_TRACKER_ARRAY = {};
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final CountQuotaTracker[] mQuotaTrackers;
+
+ private MultiRateLimiter(List<CountQuotaTracker> quotaTrackers) {
+ mQuotaTrackers = quotaTrackers.toArray(EMPTY_TRACKER_ARRAY);
+ }
+
+ /** Record that an event happened and count it towards the given quota. */
+ public void noteEvent(int userId, @NonNull String packageName, @Nullable String tag) {
+ synchronized (mLock) {
+ noteEventLocked(userId, packageName, tag);
+ }
+ }
+
+ /** Check whether the given UPTC is allowed to trigger an event. */
+ public boolean isWithinQuota(int userId, @NonNull String packageName, @Nullable String tag) {
+ synchronized (mLock) {
+ return isWithinQuotaLocked(userId, packageName, tag);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void noteEventLocked(int userId, @NonNull String packageName, @Nullable String tag) {
+ for (CountQuotaTracker quotaTracker : mQuotaTrackers) {
+ quotaTracker.noteEvent(userId, packageName, tag);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isWithinQuotaLocked(int userId, @NonNull String packageName,
+ @Nullable String tag) {
+ for (CountQuotaTracker quotaTracker : mQuotaTrackers) {
+ if (!quotaTracker.isWithinQuota(userId, packageName, tag)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** Can create a new {@link MultiRateLimiter}. */
+ public static class Builder {
+
+ private final List<CountQuotaTracker> mQuotaTrackers;
+ private final Context mContext;
+ private final Categorizer mCategorizer;
+ private final Category mCategory;
+ @Nullable private final QuotaTracker.Injector mInjector;
+
+ /**
+ * Creates a new builder and allows to inject an object that can be used
+ * to manipulate elapsed time in tests.
+ */
+ @VisibleForTesting
+ Builder(Context context, QuotaTracker.Injector injector) {
+ this.mQuotaTrackers = new ArrayList<>();
+ this.mContext = context;
+ this.mInjector = injector;
+ this.mCategorizer = Categorizer.SINGLE_CATEGORIZER;
+ this.mCategory = Category.SINGLE_CATEGORY;
+ }
+
+ /** Creates a new builder for {@link MultiRateLimiter}. */
+ public Builder(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Adds another rate limit to be used in {@link MultiRateLimiter}.
+ *
+ * @param limit The maximum event count an app can have in the rolling time window.
+ * @param windowSize The rolling time window to use when checking quota usage.
+ */
+ public Builder addRateLimit(int limit, Duration windowSize) {
+ CountQuotaTracker countQuotaTracker;
+ if (mInjector != null) {
+ countQuotaTracker = new CountQuotaTracker(mContext, mCategorizer, mInjector);
+ } else {
+ countQuotaTracker = new CountQuotaTracker(mContext, mCategorizer);
+ }
+ countQuotaTracker.setCountLimit(mCategory, limit, windowSize.toMillis());
+ mQuotaTrackers.add(countQuotaTracker);
+ return this;
+ }
+
+ /** Adds another rate limit to be used in {@link MultiRateLimiter}. */
+ public Builder addRateLimit(@NonNull RateLimit rateLimit) {
+ return addRateLimit(rateLimit.mLimit, rateLimit.mWindowSize);
+ }
+
+ /** Adds all given rate limits that will be used in {@link MultiRateLimiter}. */
+ public Builder addRateLimits(@NonNull RateLimit[] rateLimits) {
+ for (RateLimit rateLimit : rateLimits) {
+ addRateLimit(rateLimit);
+ }
+ return this;
+ }
+
+ /**
+ * Return a new {@link com.android.server.utils.quota.MultiRateLimiter} using set rate
+ * limit.
+ */
+ public MultiRateLimiter build() {
+ return new MultiRateLimiter(mQuotaTrackers);
+ }
+ }
+
+ /** Helper class that describes a rate limit. */
+ public static class RateLimit {
+ public final int mLimit;
+ public final Duration mWindowSize;
+
+ /**
+ * @param limit The maximum count of some occurrence in the rolling time window.
+ * @param windowSize The rolling time window to use when checking quota usage.
+ */
+ private RateLimit(int limit, Duration windowSize) {
+ this.mLimit = limit;
+ this.mWindowSize = windowSize;
+ }
+
+ /**
+ * @param limit The maximum count of some occurrence in the rolling time window.
+ * @param windowSize The rolling time window to use when checking quota usage.
+ */
+ public static RateLimit create(int limit, Duration windowSize) {
+ return new RateLimit(limit, windowSize);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c3d5874de609..31984531d31f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2984,7 +2984,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
if (wallpaper.allowBackup) {
- out.attribute(null, "backup", "true");
+ out.attributeBoolean(null, "backup", true);
}
out.endTag(null, tag);
@@ -3249,7 +3249,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
}
wallpaper.name = parser.getAttributeValue(null, "name");
- wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
+ wallpaper.allowBackup = parser.getAttributeBoolean(null, "backup", false);
}
// Called by SystemBackupAgent after files are restored to disk.
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
new file mode 100644
index 000000000000..9f1152c8e371
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
+import static com.android.server.wm.Task.ActivityState.DESTROYED;
+import static com.android.server.wm.Task.ActivityState.DESTROYING;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.IActivityClientController;
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.service.voice.VoiceInteractionManagerInternal;
+import android.util.Slog;
+import android.view.RemoteAnimationDefinition;
+
+import com.android.internal.app.AssistUtils;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
+import com.android.server.Watchdog;
+import com.android.server.uri.NeededUriGrants;
+import com.android.server.vr.VrManagerInternal;
+
+import java.util.Arrays;
+
+/**
+ * Server side implementation for the client activity to interact with system.
+ *
+ * @see android.app.ActivityClient
+ */
+class ActivityClientController extends IActivityClientController.Stub {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityClientController" : TAG_ATM;
+ private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+
+ private final ActivityTaskManagerService mService;
+ private final WindowManagerGlobalLock mGlobalLock;
+ private final ActivityTaskSupervisor mTaskSupervisor;
+ private final Context mContext;
+
+ /** Wrapper around VoiceInteractionServiceManager. */
+ private AssistUtils mAssistUtils;
+
+ ActivityClientController(ActivityTaskManagerService service) {
+ mService = service;
+ mGlobalLock = service.mGlobalLock;
+ mTaskSupervisor = service.mTaskSupervisor;
+ mContext = service.mContext;
+ }
+
+ void onSystemReady() {
+ mAssistUtils = new AssistUtils(mContext);
+ }
+
+ @Override
+ public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ return;
+ }
+ mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */,
+ false /* processPausingActivities */, config);
+ if (stopProfiling && r.hasProcess()) {
+ r.app.clearProfilerIfNeeded();
+ }
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void activityResumed(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ ActivityRecord.activityResumedLocked(token);
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ @Override
+ public void activityTopResumedStateLost() {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ mTaskSupervisor.handleTopResumedStateReleased(false /* timeout */);
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ @Override
+ public void activityPaused(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null) {
+ r.activityPaused(false);
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ @Override
+ public void activityStopped(IBinder token, Bundle icicle, PersistableBundle persistentState,
+ CharSequence description) {
+ if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token);
+
+ // Refuse possible leaked file descriptors.
+ if (icicle != null && icicle.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Bundle");
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+
+ String restartingName = null;
+ 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() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+ // The activity was requested to restart from
+ // {@link #restartActivityProcessIfVisible}.
+ restartingName = r.app.mName;
+ restartingUid = r.app.mUid;
+ }
+ r.activityStopped(icicle, persistentState, description);
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ if (restartingName != null) {
+ // In order to let the foreground activity can be restarted with its saved state from
+ // {@link android.app.Activity#onSaveInstanceState}, the kill operation is postponed
+ // until the activity reports stopped with the state. And the activity record will be
+ // kept because the record state is restarting, then the activity will be restarted
+ // immediately if it is still the top one.
+ mTaskSupervisor.removeRestartTimeouts(r);
+ mService.mAmInternal.killProcess(restartingName, restartingUid,
+ "restartActivityProcess");
+ }
+ mService.mAmInternal.trimApplications();
+
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ @Override
+ public void activityDestroyed(IBinder token) {
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed");
+ try {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null) {
+ r.destroyed("activityDestroyed");
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ @Override
+ public void activityRelaunched(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ mTaskSupervisor.activityRelaunchedLocked(token);
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ @Override
+ public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
+ int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
+ ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s",
+ token, Arrays.toString(horizontalSizeConfiguration),
+ Arrays.toString(verticalSizeConfigurations));
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations,
+ smallestSizeConfigurations);
+ }
+ }
+ }
+
+ /**
+ * Attempts to move a task backwards in z-order (the order of activities within the task is
+ * unchanged).
+ *
+ * There are several possible results of this call:
+ * - if the task is locked, then we will show the lock toast.
+ * - if there is a task behind the provided task, then that task is made visible and resumed as
+ * this task is moved to the back.
+ * - otherwise, if there are no other tasks in the root task:
+ * - if this task is in the pinned mode, then we remove the task completely, which will
+ * have the effect of moving the task to the top or bottom of the fullscreen root task
+ * (depending on whether it is visible).
+ * - otherwise, we simply return home and hide this task.
+ *
+ * @param token A reference to the activity we wish to move.
+ * @param nonRoot If false then this only works if the activity is the root
+ * of a task; if true it will work for any activity in a task.
+ * @return Returns true if the move completed, false if not.
+ */
+ @Override
+ public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
+ enforceNotIsolatedCaller("moveActivityTaskToBack");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
+ final Task task = mService.mRootWindowContainer.anyTaskForId(taskId);
+ if (task != null) {
+ return ActivityRecord.getStackLocked(token).moveTaskToBack(task);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
+ if (srec != null) {
+ return srec.getRootTask().shouldUpRecreateTaskLocked(srec, destAffinity);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+ Intent resultData) {
+ final ActivityRecord r;
+ synchronized (mGlobalLock) {
+ r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return false;
+ }
+ }
+
+ // Carefully collect grants without holding lock.
+ final NeededUriGrants destGrants = mService.collectGrants(destIntent, r);
+ final NeededUriGrants resultGrants = mService.collectGrants(resultData, r.resultTo);
+
+ synchronized (mGlobalLock) {
+ return r.getRootTask().navigateUpTo(
+ r, destIntent, destGrants, resultCode, resultData, resultGrants);
+ }
+ }
+
+ @Override
+ public boolean releaseActivityInstance(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null || !r.isDestroyable()) {
+ return false;
+ }
+ r.destroyImmediately("app-req");
+ return r.isState(DESTROYING, DESTROYED);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ /**
+ * This is the internal entry point for handling Activity.finish().
+ *
+ * @param token The Binder token referencing the Activity we want to finish.
+ * @param resultCode Result code, if any, from this Activity.
+ * @param resultData Result data (Intent), if any, from this Activity.
+ * @param finishTask Whether to finish the task associated with this Activity.
+ * @return Returns true if the activity successfully finished, or false if it is still running.
+ */
+ @Override
+ public boolean finishActivity(IBinder token, int resultCode, Intent resultData,
+ int finishTask) {
+ // Refuse possible leaked file descriptors.
+ if (resultData != null && resultData.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ final ActivityRecord r;
+ synchronized (mGlobalLock) {
+ r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return true;
+ }
+ }
+
+ // Carefully collect grants without holding lock.
+ final NeededUriGrants resultGrants = mService.collectGrants(resultData, r.resultTo);
+
+ synchronized (mGlobalLock) {
+ // Check again in case activity was removed when collecting grants.
+ if (!r.isInHistory()) {
+ return true;
+ }
+
+ // Keep track of the root activity of the task before we finish it.
+ final Task tr = r.getTask();
+ final ActivityRecord rootR = tr.getRootActivity();
+ if (rootR == null) {
+ Slog.w(TAG, "Finishing task with all activities already finished");
+ }
+ // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
+ // finish.
+ if (mService.getLockTaskController().activityBlockedFromFinish(r)) {
+ return false;
+ }
+
+ // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
+ // We should consolidate.
+ if (mService.mController != null) {
+ // Find the first activity that is not finishing.
+ final ActivityRecord next =
+ r.getRootTask().topRunningActivity(token, INVALID_TASK_ID);
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean resumeOK = true;
+ try {
+ resumeOK = mService.mController.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mService.mController = null;
+ Watchdog.getInstance().setActivityController(null);
+ }
+
+ if (!resumeOK) {
+ Slog.i(TAG, "Not finishing activity because controller resumed");
+ return false;
+ }
+ }
+ }
+
+ // Note down that the process has finished an activity and is in background activity
+ // starts grace period.
+ if (r.app != null) {
+ r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis());
+ }
+
+ final long origId = Binder.clearCallingIdentity();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
+ try {
+ final boolean res;
+ final boolean finishWithRootActivity =
+ finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
+ if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
+ || (finishWithRootActivity && r == rootR)) {
+ // If requested, remove the task that is associated to this activity only if it
+ // was the root activity in the task. The result code and data is ignored
+ // because we don't support returning them across task boundaries. Also, to
+ // keep backwards compatibility we remove the task from recents when finishing
+ // task with root activity.
+ mTaskSupervisor.removeTask(tr, false /*killProcess*/,
+ finishWithRootActivity, "finish-activity");
+ res = true;
+ // Explicitly dismissing the activity so reset its relaunch flag.
+ r.mRelaunchReason = RELAUNCH_REASON_NONE;
+ } else {
+ r.finishIfPossible(resultCode, resultData, resultGrants,
+ "app-request", true /* oomAdj */);
+ res = r.finishing;
+ if (!res) {
+ Slog.i(TAG, "Failed to finish by app-request");
+ }
+ }
+ return res;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ @Override
+ public boolean finishActivityAffinity(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return false;
+ }
+
+ // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
+ // can finish.
+ if (mService.getLockTaskController().activityBlockedFromFinish(r)) {
+ return false;
+ }
+
+ r.getTask().forAllActivities(activity -> r.finishIfSameAffinity(activity),
+ r /* boundary */, true /* includeBoundary */,
+ true /* traverseTopToBottom */);
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void finishSubActivity(IBinder token, String resultWho, int requestCode) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) return;
+
+ // TODO: This should probably only loop over the task since you need to be in the
+ // same task to return results.
+ r.getRootTask().forAllActivities(activity -> {
+ activity.finishIfSubActivity(r /* parent */, resultWho, requestCode);
+ }, true /* traverseTopToBottom */);
+
+ mService.updateOomAdj();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public boolean isTopOfTask(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ return r != null && r.getTask().getTopNonFinishingActivity() == r;
+ }
+ }
+
+ @Override
+ public boolean willActivityBeVisible(IBinder token) {
+ synchronized (mGlobalLock) {
+ final Task rootTask = ActivityRecord.getStackLocked(token);
+ return rootTask != null && rootTask.willActivityBeVisible(token);
+ }
+ }
+
+ @Override
+ public int getDisplayId(IBinder activityToken) {
+ synchronized (mGlobalLock) {
+ final Task rootTask = ActivityRecord.getStackLocked(activityToken);
+ if (rootTask != null) {
+ final int displayId = rootTask.getDisplayId();
+ return displayId != INVALID_DISPLAY ? displayId : DEFAULT_DISPLAY;
+ }
+ return DEFAULT_DISPLAY;
+ }
+ }
+
+ @Override
+ public int getTaskForActivity(IBinder token, boolean onlyRoot) {
+ synchronized (mGlobalLock) {
+ return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
+ }
+ }
+
+ @Override
+ public ComponentName getCallingActivity(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = getCallingRecord(token);
+ return r != null ? r.intent.getComponent() : null;
+ }
+ }
+
+ @Override
+ public String getCallingPackage(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = getCallingRecord(token);
+ return r != null ? r.info.packageName : null;
+ }
+ }
+
+ private static ActivityRecord getCallingRecord(IBinder token) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ return r != null ? r.resultTo : null;
+ }
+
+ @Override
+ public Bundle getActivityOptions(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return null;
+ }
+ final ActivityOptions activityOptions = r.takeOptionsLocked(true /* fromClient */);
+ return activityOptions != null ? activityOptions.toBundle() : null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void setRequestedOrientation(IBinder token, int requestedOrientation) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.setRequestedOrientation(requestedOrientation);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public int getRequestedOrientation(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ return r != null
+ ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+ }
+
+ @Override
+ public boolean convertFromTranslucent(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ return r != null && r.setOccludesParent(true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public boolean convertToTranslucent(IBinder token, Bundle options) {
+ final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return false;
+ }
+ final ActivityRecord under = r.getTask().getActivityBelow(r);
+ if (under != null) {
+ under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
+ }
+ return r.setOccludesParent(false);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public boolean isImmersive(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ throw new IllegalArgumentException();
+ }
+ return r.immersive;
+ }
+ }
+
+ @Override
+ public void setImmersive(IBinder token, boolean immersive) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ throw new IllegalArgumentException();
+ }
+ r.immersive = immersive;
+
+ // Update associated state if we're frontmost.
+ if (r.isFocusedActivityOnDisplay()) {
+ ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r);
+ mService.applyUpdateLockStateLocked(r);
+ }
+ }
+ }
+
+ @Override
+ public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ensureValidPictureInPictureActivityParams(
+ "enterPictureInPictureMode", token, params);
+ return mService.enterPictureInPictureMode(r, params);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ensureValidPictureInPictureActivityParams(
+ "setPictureInPictureParams", token, params);
+
+ // Only update the saved args from the args that are set.
+ r.setPictureInPictureParams(params);
+ if (r.inPinnedWindowingMode()) {
+ // If the activity is already in picture-in-picture, update the pinned task now
+ // if it is not already expanding to fullscreen. Otherwise, the arguments will
+ // be used the next time the activity enters PiP.
+ final Task rootTask = r.getRootTask();
+ rootTask.setPictureInPictureAspectRatio(
+ r.pictureInPictureArgs.getAspectRatio());
+ rootTask.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ /**
+ * Checks the state of the system and the activity associated with the given {@param token} to
+ * verify that picture-in-picture is supported for that activity.
+ *
+ * @return the activity record for the given {@param token} if all the checks pass.
+ */
+ private ActivityRecord ensureValidPictureInPictureActivityParams(String caller,
+ IBinder token, PictureInPictureParams params) {
+ if (!mService.mSupportsPictureInPicture) {
+ throw new IllegalStateException(caller
+ + ": Device doesn't support picture-in-picture mode.");
+ }
+
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ throw new IllegalStateException(caller
+ + ": Can't find activity for token=" + token);
+ }
+
+ if (!r.supportsPictureInPicture()) {
+ throw new IllegalStateException(caller
+ + ": Current activity does not support picture-in-picture.");
+ }
+
+ if (params.hasSetAspectRatio()
+ && !mService.mWindowManager.isValidPictureInPictureAspectRatio(
+ r.mDisplayContent, params.getAspectRatio())) {
+ final float minAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+ final float maxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+ throw new IllegalArgumentException(String.format(caller
+ + ": Aspect ratio is too extreme (must be between %f and %f).",
+ minAspectRatio, maxAspectRatio));
+ }
+
+ // Truncate the number of actions if necessary.
+ params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext));
+ return r;
+ }
+
+ @Override
+ public void toggleFreeformWindowingMode(IBinder token) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ throw new IllegalArgumentException(
+ "toggleFreeformWindowingMode: No activity record matching token="
+ + token);
+ }
+
+ final Task rootTask = r.getRootTask();
+ if (rootTask == null) {
+ throw new IllegalStateException("toggleFreeformWindowingMode: the activity "
+ + "doesn't have a root task");
+ }
+
+ if (!rootTask.inFreeformWindowingMode()
+ && rootTask.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+ throw new IllegalStateException("toggleFreeformWindowingMode: You can only "
+ + "toggle between fullscreen and freeform.");
+ }
+
+ if (rootTask.inFreeformWindowingMode()) {
+ rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ } else if (!mService.mSizeCompatFreeform && r.inSizeCompatMode()) {
+ throw new IllegalStateException("Size-compat windows are currently not"
+ + "freeform-enabled");
+ } else if (rootTask.getParent().inFreeformWindowingMode()) {
+ // If the window is on a freeform display, set it to undefined. It will be
+ // resolved to freeform and it can adjust windowing mode when the display mode
+ // changes in runtime.
+ rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ } else {
+ rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void startLockTaskModeByToken(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null) {
+ mService.startLockTaskMode(r.getTask(), false /* isSystemCaller */);
+ }
+ }
+ }
+
+ @Override
+ public void stopLockTaskModeByToken(IBinder token) {
+ mService.stopLockTaskModeInternal(token, false /* isSystemCaller */);
+ }
+
+ @Override
+ public void showLockTaskEscapeMessage(IBinder token) {
+ synchronized (mGlobalLock) {
+ if (ActivityRecord.forTokenLocked(token) != null) {
+ mService.getLockTaskController().showLockTaskToast();
+ }
+ }
+ }
+
+ @Override
+ public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.setTaskDescription(td);
+ }
+ }
+ }
+
+ @Override
+ public boolean showAssistFromActivity(IBinder token, Bundle args) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord caller = ActivityRecord.forTokenLocked(token);
+ final Task topRootTask = mService.getTopDisplayFocusedRootTask();
+ final ActivityRecord top = topRootTask != null
+ ? topRootTask.getTopNonFinishingActivity() : null;
+ if (top != caller) {
+ Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ + " is not current top " + top);
+ return false;
+ }
+ if (!top.nowVisible) {
+ Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ + " is not visible");
+ return false;
+ }
+ }
+ return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION,
+ null /* showCallback */, token);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public boolean isRootVoiceInteraction(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ return r != null && r.rootVoiceInteraction;
+ }
+ }
+
+ @Override
+ public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+ Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+ synchronized (mGlobalLock) {
+ final Task topRootTask = mService.getTopDisplayFocusedRootTask();
+ final ActivityRecord activity = topRootTask != null
+ ? topRootTask.getTopNonFinishingActivity() : null;
+ if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
+ throw new SecurityException("Only focused activity can call startVoiceInteraction");
+ }
+ if (mService.mRunningVoice != null || activity.getTask().voiceSession != null
+ || activity.voiceSession != null) {
+ Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
+ return;
+ }
+ if (activity.pendingVoiceInteractionStart) {
+ Slog.w(TAG, "Pending start of voice interaction already.");
+ return;
+ }
+ activity.pendingVoiceInteractionStart = true;
+ }
+ LocalServices.getService(VoiceInteractionManagerInternal.class)
+ .startLocalVoiceInteraction(callingActivity, options);
+ }
+
+ @Override
+ public void stopLocalVoiceInteraction(IBinder callingActivity) {
+ LocalServices.getService(VoiceInteractionManagerInternal.class)
+ .stopLocalVoiceInteraction(callingActivity);
+ }
+
+ @Override
+ public void setShowWhenLocked(IBinder token, boolean showWhenLocked) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.setShowWhenLocked(showWhenLocked);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.setInheritShowWhenLocked(inheritShowWhenLocked);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.setTurnScreenOn(turnScreenOn);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.reportFullyDrawnLocked(restoredFromBundle);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void overridePendingTransition(IBinder token, String packageName,
+ int enterAnim, int exitAnim) {
+ final long origId = Binder.clearCallingIdentity();
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
+ r.mDisplayContent.mAppTransition.overridePendingAppTransition(
+ packageName, enterAnim, exitAnim, null, null);
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ @Override
+ public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
+ mService.enforceSystemHasVrFeature();
+
+ final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+ final ActivityRecord r;
+ synchronized (mGlobalLock) {
+ r = ActivityRecord.isInStackLocked(token);
+ }
+ if (r == null) {
+ throw new IllegalArgumentException();
+ }
+
+ final int err;
+ if ((err = vrService.hasVrPackage(packageName, r.mUserId)) != VrManagerInternal.NO_ERROR) {
+ return err;
+ }
+
+ // Clear the binder calling uid since this path may call moveToTask().
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ r.requestedVrComponent = (enabled) ? packageName : null;
+
+ // Update associated state if this activity is currently focused.
+ if (r.isFocusedActivityOnDisplay()) {
+ mService.applyUpdateVrModeLocked(r);
+ }
+ return 0;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
+ public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.setDisablePreviewScreenshots(disable);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
+ mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "registerRemoteAnimations");
+ definition.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid());
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.registerRemoteAnimations(definition);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void unregisterRemoteAnimations(IBinder token) {
+ mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "unregisterRemoteAnimations");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r != null) {
+ r.unregisterRemoteAnimations();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(IBinder token) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ if (mService.mWindowOrganizerController.mTaskOrganizerController
+ .handleInterceptBackPressedOnTaskRoot(r.getRootTask())) {
+ // This task is handled by a task organizer that has requested the back pressed
+ // callback.
+ } else {
+ moveActivityTaskToBack(token, false /* nonRoot */);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1b8cc082f598..743796b0aad0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -925,8 +925,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mVoiceInteraction) {
pw.println(prefix + "mVoiceInteraction=true");
}
- pw.print(prefix); pw.print("mOccludesParent="); pw.print(mOccludesParent);
- pw.print(" mOrientation="); pw.println(mOrientation);
+ pw.print(prefix); pw.print("mOccludesParent="); pw.println(mOccludesParent);
+ pw.print(prefix); pw.print("mOrientation=");
+ pw.println(ActivityInfo.screenOrientationToString(mOrientation));
pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
+ " mVisible=" + mVisible + " mClientVisible=" + mClientVisible
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -1011,6 +1012,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (info.supportsSizeChanges) {
pw.println(prefix + "supportsSizeChanges=true");
}
+ if (info.configChanges != 0) {
+ pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
+ }
}
}
@@ -4032,7 +4036,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mDisplayContent != null) {
mDisplayContent.setLayoutNeeded();
}
- mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+ mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, this).sendToTarget();
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3710120a7934..8298dfd85114 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -33,8 +33,6 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -62,7 +60,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIV
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
import static android.provider.Settings.System.FONT_SCALE;
-import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -95,15 +92,8 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr
import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
import static com.android.server.am.EventLogTags.writeConfigurationChanged;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IMMERSIVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
@@ -121,8 +111,6 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_P
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_ONLY;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -132,7 +120,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
@@ -143,6 +130,7 @@ import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.Dialog;
+import android.app.IActivityClientController;
import android.app.IActivityController;
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
@@ -197,7 +185,6 @@ import android.os.IUserManager;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
-import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
@@ -237,7 +224,6 @@ import android.window.WindowContainerTransaction;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -249,8 +235,6 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AttributeCache;
import com.android.server.LocalServices;
@@ -271,7 +255,6 @@ import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.vr.VrManagerInternal;
import java.io.BufferedReader;
import java.io.File;
@@ -308,11 +291,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
- private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
- private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
- private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
- private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
- private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
// How long we wait until we timeout on key dispatching during instrumentation.
static final long INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS = 60 * 1000;
@@ -385,6 +363,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
final Object mGlobalLockWithoutBoost = mGlobalLock;
ActivityTaskSupervisor mTaskSupervisor;
+ ActivityClientController mActivityClientController;
RootWindowContainer mRootWindowContainer;
WindowManagerService mWindowManager;
private UserManagerService mUserManager;
@@ -418,9 +397,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** State of external calls telling us if the device is awake or asleep. */
private boolean mKeyguardShown = false;
- // Wrapper around VoiceInteractionServiceManager
- private AssistUtils mAssistUtils;
-
// VoiceInteraction session ID that changes for each new request except when
// being called for multi-window assist in a single session.
private int mViSessionId = 1000;
@@ -763,10 +739,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final PackageManager pm = mContext.getPackageManager();
mHasHeavyWeightFeature = pm.hasSystemFeature(FEATURE_CANT_SAVE_STATE);
mHasLeanbackFeature = pm.hasSystemFeature(FEATURE_LEANBACK);
- mAssistUtils = new AssistUtils(mContext);
mVrController.onSystemReady();
mRecentTasks.onSystemReadyLocked();
mTaskSupervisor.onSystemReady();
+ mActivityClientController.onSystemReady();
mBlockActivityAfterHomeEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG, false);
@@ -872,6 +848,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mCompatModePackages = new CompatModePackages(this, systemDir, mH);
mPendingIntentController = intentController;
mTaskSupervisor = createTaskSupervisor();
+ mActivityClientController = new ActivityClientController(this);
mTaskChangeNotificationController =
new TaskChangeNotificationController(mGlobalLock, mTaskSupervisor, mH);
@@ -1698,311 +1675,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mAmInternal.getActivityInfoForUser(aInfo, userId);
}
- /**
- * This is the internal entry point for handling Activity.finish().
- *
- * @param token The Binder token referencing the Activity we want to finish.
- * @param resultCode Result code, if any, from this Activity.
- * @param resultData Result data (Intent), if any, from this Activity.
- * @param finishTask Whether to finish the task associated with this Activity.
- * @return Returns true if the activity successfully finished, or false if it is still running.
- */
- @Override
- public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
- int finishTask) {
- // Refuse possible leaked file descriptors
- if (resultData != null && resultData.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
-
- final ActivityRecord r;
- synchronized (mGlobalLock) {
- r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return true;
- }
- }
-
- // Carefully collect grants without holding lock
- final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo);
-
- synchronized (mGlobalLock) {
- // Sanity check in case activity was removed before entering global lock.
- if (!r.isInHistory()) {
- return true;
- }
-
- // Keep track of the root activity of the task before we finish it
- final Task tr = r.getTask();
- final ActivityRecord rootR = tr.getRootActivity();
- if (rootR == null) {
- Slog.w(TAG, "Finishing task with all activities already finished");
- }
- // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
- // finish.
- if (getLockTaskController().activityBlockedFromFinish(r)) {
- return false;
- }
-
- // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked
- // We should consolidate.
- if (mController != null) {
- // Find the first activity that is not finishing.
- final ActivityRecord next =
- r.getRootTask().topRunningActivity(token, INVALID_TASK_ID);
- if (next != null) {
- // ask watcher if this is allowed
- boolean resumeOK = true;
- try {
- resumeOK = mController.activityResuming(next.packageName);
- } catch (RemoteException e) {
- mController = null;
- Watchdog.getInstance().setActivityController(null);
- }
-
- if (!resumeOK) {
- Slog.i(TAG, "Not finishing activity because controller resumed");
- return false;
- }
- }
- }
-
- // note down that the process has finished an activity and is in background activity
- // starts grace period
- if (r.app != null) {
- r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis());
- }
-
- final long origId = Binder.clearCallingIdentity();
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
- try {
- boolean res;
- final boolean finishWithRootActivity =
- finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
- if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
- || (finishWithRootActivity && r == rootR)) {
- // If requested, remove the task that is associated to this activity only if it
- // was the root activity in the task. The result code and data is ignored
- // because we don't support returning them across task boundaries. Also, to
- // keep backwards compatibility we remove the task from recents when finishing
- // task with root activity.
- mTaskSupervisor.removeTask(tr, false /*killProcess*/,
- finishWithRootActivity, "finish-activity");
- res = true;
- // Explicitly dismissing the activity so reset its relaunch flag.
- r.mRelaunchReason = RELAUNCH_REASON_NONE;
- } else {
- r.finishIfPossible(resultCode, resultData, resultGrants,
- "app-request", true /* oomAdj */);
- res = r.finishing;
- if (!res) {
- Slog.i(TAG, "Failed to finish by app-request");
- }
- }
- return res;
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public boolean finishActivityAffinity(IBinder token) {
- synchronized (mGlobalLock) {
- final long origId = Binder.clearCallingIdentity();
- try {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return false;
- }
-
- // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
- // can finish.
- if (getLockTaskController().activityBlockedFromFinish(r)) {
- return false;
- }
-
- final PooledFunction p = PooledLambda.obtainFunction(
- ActivityRecord::finishIfSameAffinity, r,
- PooledLambda.__(ActivityRecord.class));
- r.getTask().forAllActivities(
- p, r, true /*includeBoundary*/, true /*traverseTopToBottom*/);
- p.recycle();
-
- return true;
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- return;
- }
- mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */,
- false /* processPausingActivities */, config);
- if (stopProfiling && r.hasProcess()) {
- r.app.clearProfilerIfNeeded();
- }
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public final void activityResumed(IBinder token) {
- final long origId = Binder.clearCallingIdentity();
- synchronized (mGlobalLock) {
- ActivityRecord.activityResumedLocked(token);
- }
- Binder.restoreCallingIdentity(origId);
- }
-
- @Override
- public final void activityTopResumedStateLost() {
- final long origId = Binder.clearCallingIdentity();
- synchronized (mGlobalLock) {
- mTaskSupervisor.handleTopResumedStateReleased(false /* timeout */);
- }
- Binder.restoreCallingIdentity(origId);
- }
-
- @Override
- public final void activityPaused(IBinder token) {
- final long origId = Binder.clearCallingIdentity();
- synchronized (mGlobalLock) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null) {
- r.activityPaused(false);
- }
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- Binder.restoreCallingIdentity(origId);
- }
-
- @Override
- public final void activityStopped(IBinder token, Bundle icicle,
- PersistableBundle persistentState, CharSequence description) {
- if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token);
-
- // Refuse possible leaked file descriptors
- if (icicle != null && icicle.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Bundle");
- }
-
- final long origId = Binder.clearCallingIdentity();
-
- String restartingName = null;
- 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()
- && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
- // The activity was requested to restart from
- // {@link #restartActivityProcessIfVisible}.
- restartingName = r.app.mName;
- restartingUid = r.app.mUid;
- }
- r.activityStopped(icicle, persistentState, description);
- }
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
-
- if (restartingName != null) {
- // In order to let the foreground activity can be restarted with its saved state from
- // {@link android.app.Activity#onSaveInstanceState}, the kill operation is postponed
- // until the activity reports stopped with the state. And the activity record will be
- // kept because the record state is restarting, then the activity will be restarted
- // immediately if it is still the top one.
- mTaskSupervisor.removeRestartTimeouts(r);
- mAmInternal.killProcess(restartingName, restartingUid, "restartActivityProcess");
- }
- mAmInternal.trimApplications();
-
- Binder.restoreCallingIdentity(origId);
- }
-
- @Override
- public final void activityDestroyed(IBinder token) {
- 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);
- }
- }
- }
-
- @Override
- public final void activityRelaunched(IBinder token) {
- final long origId = Binder.clearCallingIdentity();
- synchronized (mGlobalLock) {
- mTaskSupervisor.activityRelaunchedLocked(token);
- }
- Binder.restoreCallingIdentity(origId);
- }
-
@Override
- public void setRequestedOrientation(IBinder token, int requestedOrientation) {
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- r.setRequestedOrientation(requestedOrientation);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public int getRequestedOrientation(IBinder token) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- return (r != null)
- ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- }
- }
-
- @Override
- public void setImmersive(IBinder token, boolean immersive) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- throw new IllegalArgumentException();
- }
- r.immersive = immersive;
-
- // update associated state if we're frontmost
- if (r.isFocusedActivityOnDisplay()) {
- ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r);
- applyUpdateLockStateLocked(r);
- }
- }
+ public IActivityClientController getActivityClientController() {
+ return mActivityClientController;
}
void applyUpdateLockStateLocked(ActivityRecord r) {
@@ -2025,17 +1700,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public boolean isImmersive(IBinder token) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- throw new IllegalArgumentException();
- }
- return r.immersive;
- }
- }
-
- @Override
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("isTopActivityImmersive");
synchronized (mGlobalLock) {
@@ -2050,27 +1714,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void overridePendingTransition(IBinder token, String packageName,
- int enterAnim, int exitAnim) {
- synchronized (mGlobalLock) {
- ActivityRecord self = ActivityRecord.isInStackLocked(token);
- if (self == null) {
- return;
- }
-
- final long origId = Binder.clearCallingIdentity();
-
- if (self.isState(
- Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
- self.mDisplayContent.mAppTransition.overridePendingAppTransition(
- packageName, enterAnim, exitAnim, null, null);
- }
-
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (mGlobalLock) {
@@ -2125,77 +1768,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public boolean convertFromTranslucent(IBinder token) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return false;
- }
- return r.setOccludesParent(true);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public boolean convertToTranslucent(IBinder token, Bundle options) {
- SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return false;
- }
- final ActivityRecord under = r.getTask().getActivityBelow(r);
- if (under != null) {
- under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
- }
- return r.setOccludesParent(false);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public void notifyActivityDrawn(IBinder token) {
- if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
- synchronized (mGlobalLock) {
- ActivityRecord r = mRootWindowContainer.isInAnyTask(token);
- if (r != null) {
- r.getRootTask().notifyActivityDrawnLocked(r);
- }
- }
- }
-
- @Override
- public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- r.reportFullyDrawnLocked(restoredFromBundle);
- }
- }
-
- @Override
- public int getDisplayId(IBinder activityToken) throws RemoteException {
- synchronized (mGlobalLock) {
- final Task stack = ActivityRecord.getStackLocked(activityToken);
- if (stack != null) {
- final int displayId = stack.getDisplayId();
- return displayId != INVALID_DISPLAY ? displayId : DEFAULT_DISPLAY;
- }
- return DEFAULT_DISPLAY;
- }
- }
-
- @Override
public RootTaskInfo getFocusedRootTaskInfo() throws RemoteException {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "getFocusedRootTaskInfo()");
final long ident = Binder.clearCallingIdentity();
@@ -2312,75 +1884,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
- synchronized (mGlobalLock) {
- final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
- if (srec != null) {
- return srec.getRootTask().shouldUpRecreateTaskLocked(srec, destAffinity);
- }
- }
- return false;
- }
-
- @Override
- public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
- Intent resultData) {
- final ActivityRecord r;
- synchronized (mGlobalLock) {
- r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return false;
- }
- }
-
- // Carefully collect grants without holding lock
- final NeededUriGrants destGrants = collectGrants(destIntent, r);
- final NeededUriGrants resultGrants = collectGrants(resultData, r.resultTo);
-
- synchronized (mGlobalLock) {
- return r.getRootTask().navigateUpTo(
- r, destIntent, destGrants, resultCode, resultData, resultGrants);
- }
- }
-
- /**
- * Attempts to move a task backwards in z-order (the order of activities within the task is
- * unchanged).
- *
- * There are several possible results of this call:
- * - if the task is locked, then we will show the lock toast
- * - if there is a task behind the provided task, then that task is made visible and resumed as
- * this task is moved to the back
- * - otherwise, if there are no other tasks in the stack:
- * - if this task is in the pinned stack, then we remove the stack completely, which will
- * have the effect of moving the task to the top or bottom of the fullscreen stack
- * (depending on whether it is visible)
- * - otherwise, we simply return home and hide this task
- *
- * @param token A reference to the activity we wish to move
- * @param nonRoot If false then this only works if the activity is the root
- * of a task; if true it will work for any activity in a task.
- * @return Returns true if the move completed, false if not.
- */
- @Override
- public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
- enforceNotIsolatedCaller("moveActivityTaskToBack");
- synchronized (mGlobalLock) {
- final long origId = Binder.clearCallingIdentity();
- try {
- int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
- final Task task = mRootWindowContainer.anyTaskForId(taskId);
- if (task != null) {
- return ActivityRecord.getStackLocked(token).moveTaskToBack(task);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- return false;
- }
-
- @Override
public Rect getTaskBounds(int taskId) {
enforceTaskPermission("getTaskBounds()");
final long ident = Binder.clearCallingIdentity();
@@ -2467,31 +1970,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- @Override
- public String getCallingPackage(IBinder token) {
- synchronized (mGlobalLock) {
- ActivityRecord r = getCallingRecordLocked(token);
- return r != null ? r.info.packageName : null;
- }
- }
-
- @Override
- public ComponentName getCallingActivity(IBinder token) {
- synchronized (mGlobalLock) {
- ActivityRecord r = getCallingRecordLocked(token);
- return r != null ? r.intent.getComponent() : null;
- }
- }
-
- private ActivityRecord getCallingRecordLocked(IBinder token) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return null;
- }
- return r.resultTo;
- }
-
- private NeededUriGrants collectGrants(Intent intent, ActivityRecord target) {
+ NeededUriGrants collectGrants(Intent intent, ActivityRecord target) {
if (target != null) {
return mUgmInternal.checkGrantUriPermissionFromIntent(intent,
Binder.getCallingUid(), target.packageName, target.mUserId);
@@ -2518,25 +1997,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- @Override
- public void onBackPressedOnTaskRoot(IBinder token) {
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- Task stack = r.getRootTask();
- final TaskOrganizerController taskOrgController =
- mWindowOrganizerController.mTaskOrganizerController;
- if (taskOrgController.handleInterceptBackPressedOnTaskRoot(stack)) {
- // This task is handled by a task organizer that has requested the back pressed
- // callback
- } else {
- moveActivityTaskToBack(token, false /* nonRoot */);
- }
- }
- }
-
/**
* TODO: Add mController hook
*/
@@ -2731,13 +2191,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- @Override
- public int getTaskForActivity(IBinder token, boolean onlyRoot) {
- synchronized (mGlobalLock) {
- return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
- }
- }
-
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
return getTasks(maxNum, false /* filterForVisibleRecents */);
}
@@ -2772,40 +2225,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public final void finishSubActivity(IBinder token, String resultWho, int requestCode) {
- synchronized (mGlobalLock) {
- final long origId = Binder.clearCallingIdentity();
- try {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) return;
-
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityRecord::finishIfSubActivity, PooledLambda.__(ActivityRecord.class),
- r, resultWho, requestCode);
- // TODO: This should probably only loop over the task since you need to be in the
- // same task to return results.
- r.getRootTask().forAllActivities(c);
- c.recycle();
-
- updateOomAdj();
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public boolean willActivityBeVisible(IBinder token) {
- synchronized (mGlobalLock) {
- Task stack = ActivityRecord.getStackLocked(token);
- if (stack != null) {
- return stack.willActivityBeVisible(token);
- }
- return false;
- }
- }
-
- @Override
public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "moveTaskToRootTask()");
synchronized (mGlobalLock) {
@@ -3035,17 +2454,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void startLockTaskModeByToken(IBinder token) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- return;
- }
- startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
- }
- }
-
- @Override
public void startSystemLockTaskMode(int taskId) {
enforceTaskPermission("startSystemLockTaskMode");
// This makes inner call to look as if it was initiated by system.
@@ -3060,18 +2468,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// When starting lock task mode the stack must be in front and focused
task.getRootTask().moveToFront("startSystemLockTaskMode");
- startLockTaskModeLocked(task, true /* isSystemCaller */);
+ startLockTaskMode(task, true /* isSystemCaller */);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- @Override
- public void stopLockTaskModeByToken(IBinder token) {
- stopLockTaskModeInternal(token, false /* isSystemCaller */);
- }
-
/**
* This API should be called by SystemUI only when user perform certain action to dismiss
* lock task mode. We should only dismiss pinned lock task mode in this case.
@@ -3082,8 +2485,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
stopLockTaskModeInternal(null, true /* isSystemCaller */);
}
- private void startLockTaskModeLocked(@Nullable Task task, boolean isSystemCaller) {
- ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskModeLocked: %s", task);
+ void startLockTaskMode(@Nullable Task task, boolean isSystemCaller) {
+ ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskMode: %s", task);
if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
return;
}
@@ -3111,7 +2514,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- private void stopLockTaskModeInternal(@Nullable IBinder token, boolean isSystemCaller) {
+ void stopLockTaskModeInternal(@Nullable IBinder token, boolean isSystemCaller) {
final int callingUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
@@ -3163,34 +2566,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r != null) {
- r.setTaskDescription(td);
- }
- }
- }
-
- @Override
- public Bundle getActivityOptions(IBinder token) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r != null) {
- final ActivityOptions activityOptions = r.takeOptionsLocked(
- true /* fromClient */);
- return activityOptions == null ? null : activityOptions.toBundle();
- }
- return null;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
public List<IBinder> getAppTasks(String callingPackage) {
int callingUid = Binder.getCallingUid();
assertPackageMatchesCallingUid(callingPackage);
@@ -3220,39 +2595,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public boolean isTopOfTask(IBinder token) {
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- return r != null && r.getTask().getTopNonFinishingActivity() == r;
- }
- }
-
- @Override
- public void notifyLaunchTaskBehindComplete(IBinder token) {
- mTaskSupervisor.scheduleLaunchTaskBehindComplete(token);
- }
-
- @Override
- public void notifyEnterAnimationComplete(IBinder token) {
- mH.post(() -> {
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null && r.attachedToProcess()) {
- try {
- r.app.getThread().scheduleEnterAnimationComplete(r.appToken);
- } catch (RemoteException e) {
- }
- }
- }
-
- });
- }
-
- /** Called from an app when assist data is ready. */
- @Override
- public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
- AssistContent content, Uri referrer) {
- PendingAssistExtras pae = (PendingAssistExtras) token;
+ public void reportAssistContextExtras(IBinder assistToken, Bundle extras,
+ AssistStructure structure, AssistContent content, Uri referrer) {
+ final PendingAssistExtras pae = (PendingAssistExtras) assistToken;
synchronized (pae) {
pae.result = extras;
pae.structure = structure;
@@ -3440,23 +2785,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public boolean releaseActivityInstance(IBinder token) {
- synchronized (mGlobalLock) {
- final long origId = Binder.clearCallingIdentity();
- try {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null || !r.isDestroyable()) {
- return false;
- }
- r.destroyImmediately("app-req");
- return r.isState(DESTROYING, DESTROYED);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
public void releaseSomeActivities(IApplicationThread appInt) {
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
@@ -3540,49 +2868,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- @Override
- public void toggleFreeformWindowingMode(IBinder token) {
- synchronized (mGlobalLock) {
- final long ident = Binder.clearCallingIdentity();
- try {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- throw new IllegalArgumentException(
- "toggleFreeformWindowingMode: No activity record matching token="
- + token);
- }
-
- final Task stack = r.getRootTask();
- if (stack == null) {
- throw new IllegalStateException("toggleFreeformWindowingMode: the activity "
- + "doesn't have a stack");
- }
-
- if (!stack.inFreeformWindowingMode()
- && stack.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
- throw new IllegalStateException("toggleFreeformWindowingMode: You can only "
- + "toggle between fullscreen and freeform.");
- }
-
- if (stack.inFreeformWindowingMode()) {
- stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- } else if (!mSizeCompatFreeform && r.inSizeCompatMode()) {
- throw new IllegalStateException("Size-compat windows are currently not"
- + "freeform-enabled");
- } else if (stack.getParent().inFreeformWindowingMode()) {
- // If the window is on a freeform display, set it to undefined. It will be
- // resolved to freeform and it can adjust windowing mode when the display mode
- // changes in runtime.
- stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- } else {
- stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) {
@@ -3884,42 +3169,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return DevicePolicyCache.getInstance().isScreenCaptureAllowed(userId, false);
}
- @Override
- public boolean showAssistFromActivity(IBinder token, Bundle args) {
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- ActivityRecord caller = ActivityRecord.forTokenLocked(token);
- ActivityRecord top = getTopDisplayFocusedRootTask().getTopNonFinishingActivity();
- if (top != caller) {
- Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
- + " is not current top " + top);
- return false;
- }
- if (!top.nowVisible) {
- Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
- + " is not visible");
- return false;
- }
- }
- return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
- token);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public boolean isRootVoiceInteraction(IBinder token) {
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return false;
- }
- return r.rootVoiceInteraction;
- }
- }
-
private void onLocalVoiceInteractionStartedLocked(IBinder activity,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
@@ -4001,17 +3250,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void showLockTaskEscapeMessage(IBinder token) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- return;
- }
- getLockTaskController().showLockTaskToast();
- }
- }
-
- @Override
public void keyguardGoingAway(int flags) {
enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
@@ -4025,22 +3263,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
- int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
- ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s",
- token, Arrays.toString(horizontalSizeConfiguration),
- Arrays.toString(verticalSizeConfigurations));
- synchronized (mGlobalLock) {
- ActivityRecord record = ActivityRecord.isInStackLocked(token);
- if (record == null) {
- return;
- }
- record.setSizeConfigurations(horizontalSizeConfiguration,
- verticalSizeConfigurations, smallestSizeConfigurations);
- }
- }
-
- @Override
public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,
"suppressResizeConfigChanges()");
@@ -4138,100 +3360,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return true;
}
- @Override
- public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
- "enterPictureInPictureMode", token, params);
- return enterPictureInPictureMode(r, params);
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
- "setPictureInPictureParams", token, params);
-
- // Only update the saved args from the args that are set
- r.setPictureInPictureParams(params);
- if (r.inPinnedWindowingMode()) {
- // If the activity is already in picture-in-picture, update the pinned stack now
- // if it is not already expanding to fullscreen. Otherwise, the arguments will
- // be used the next time the activity enters PiP
- final Task stack = r.getRootTask();
- stack.setPictureInPictureAspectRatio(
- r.pictureInPictureArgs.getAspectRatio());
- stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- /**
- * Checks the state of the system and the activity associated with the given {@param token} to
- * verify that picture-in-picture is supported for that activity.
- *
- * @return the activity record for the given {@param token} if all the checks pass.
- */
- private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
- IBinder token, PictureInPictureParams params) {
- if (!mSupportsPictureInPicture) {
- throw new IllegalStateException(caller
- + ": Device doesn't support picture-in-picture mode.");
- }
-
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
- throw new IllegalStateException(caller
- + ": Can't find activity for token=" + token);
- }
-
- if (!r.supportsPictureInPicture()) {
- throw new IllegalStateException(caller
- + ": Current activity does not support picture-in-picture.");
- }
-
- if (params.hasSetAspectRatio()
- && !mWindowManager.isValidPictureInPictureAspectRatio(
- r.mDisplayContent, params.getAspectRatio())) {
- final float minAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
- final float maxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
- throw new IllegalArgumentException(String.format(caller
- + ": Aspect ratio is too extreme (must be between %f and %f).",
- minAspectRatio, maxAspectRatio));
- }
-
- // Truncate the number of actions if necessary
- params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext));
-
- return r;
- }
-
- @Override
- public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
- enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
- synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
- if (r == null) {
- throw new IllegalArgumentException("Activity does not exist; token="
- + activityToken);
- }
- return r.getUriPermissionsLocked().getExternalToken();
- }
- }
-
// TODO(b/149338177): remove when CTS no-longer requires it
@Override
public void resizePrimarySplitScreen(Rect dockedBounds, Rect tempDockedTaskBounds,
@@ -4304,73 +3432,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
- enforceSystemHasVrFeature();
-
- final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-
- ActivityRecord r;
- synchronized (mGlobalLock) {
- r = ActivityRecord.isInStackLocked(token);
- }
-
- if (r == null) {
- throw new IllegalArgumentException();
- }
-
- int err;
- if ((err = vrService.hasVrPackage(packageName, r.mUserId)) !=
- VrManagerInternal.NO_ERROR) {
- return err;
- }
-
- // Clear the binder calling uid since this path may call moveToTask().
- final long callingId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- r.requestedVrComponent = (enabled) ? packageName : null;
-
- // Update associated state if this activity is currently focused
- if (r.isFocusedActivityOnDisplay()) {
- applyUpdateVrModeLocked(r);
- }
- return 0;
- }
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
- @Override
- public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
- Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
- synchronized (mGlobalLock) {
- ActivityRecord activity = getTopDisplayFocusedRootTask().getTopNonFinishingActivity();
- if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
- throw new SecurityException("Only focused activity can call startVoiceInteraction");
- }
- if (mRunningVoice != null || activity.getTask().voiceSession != null
- || activity.voiceSession != null) {
- Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
- return;
- }
- if (activity.pendingVoiceInteractionStart) {
- Slog.w(TAG, "Pending start of voice interaction already.");
- return;
- }
- activity.pendingVoiceInteractionStart = true;
- }
- LocalServices.getService(VoiceInteractionManagerInternal.class)
- .startLocalVoiceInteraction(callingActivity, options);
- }
-
- @Override
- public void stopLocalVoiceInteraction(IBinder callingActivity) {
- LocalServices.getService(VoiceInteractionManagerInternal.class)
- .stopLocalVoiceInteraction(callingActivity);
- }
-
- @Override
public boolean supportsLocalVoiceInteraction() {
return LocalServices.getService(VoiceInteractionManagerInternal.class)
.supportsLocalVoiceInteraction();
@@ -4474,24 +3535,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token="
- + token);
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- r.setDisablePreviewScreenshots(disable);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
public void invalidateHomeTaskSnapshot(IBinder token) {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
@@ -4532,91 +3575,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void setShowWhenLocked(IBinder token, boolean showWhenLocked) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- r.setShowWhenLocked(showWhenLocked);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- r.setInheritShowWhenLocked(inheritShowWhenLocked);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- r.setTurnScreenOn(turnScreenOn);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
- mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
- "registerRemoteAnimations");
- definition.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid());
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- r.registerRemoteAnimations(definition);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
- public void unregisterRemoteAnimations(IBinder token) {
- mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
- "unregisterRemoteAnimations");
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return;
- }
- final long origId = Binder.clearCallingIdentity();
- try {
- r.unregisterRemoteAnimations();
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-
- @Override
public void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
@@ -4743,7 +3701,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mVrController.shouldDisableNonVrUiLocked();
}
- private void applyUpdateVrModeLocked(ActivityRecord r) {
+ void applyUpdateVrModeLocked(ActivityRecord r) {
// VR apps are expected to run in a main display. If an app is turning on VR for
// itself, but isn't on the main display, then move it there before enabling VR Mode.
if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
@@ -5097,7 +4055,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mAmInternal.getCurrentUserId();
}
- private void enforceNotIsolatedCaller(String caller) {
+ static void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 55200b566979..994f07959f3b 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -31,16 +31,12 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -541,15 +537,7 @@ class AppWarnings {
if ("package".equals(tagName)) {
final String name = parser.getAttributeValue(null, "name");
if (name != null) {
- final String flags = parser.getAttributeValue(
- null, "flags");
- int flagsInt = 0;
- if (flags != null) {
- try {
- flagsInt = Integer.parseInt(flags);
- } catch (NumberFormatException e) {
- }
- }
+ int flagsInt = parser.getAttributeInt(null, "flags", 0);
mPackageFlags.put(name, flagsInt);
}
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
index 3d84e1752e6a..ce6b7f917991 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
@@ -23,7 +23,7 @@ import android.content.res.Configuration;
*/
public interface ConfigurationContainerListener {
- /** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */
+ /** @see ConfigurationContainer#onRequestedOverrideConfigurationChanged */
default void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {}
/** Called when new merged override configuration is reported. */
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index a4ac16f2ec77..15483cb90ce2 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -174,6 +174,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return false;
}
+ if (mDisplayContent.mFocusedApp != null) {
+ // We record the last focused TDA that respects orientation request, check if this
+ // change may affect it.
+ mDisplayContent.onLastFocusedTaskDisplayAreaChanged(
+ mDisplayContent.mFocusedApp.getDisplayArea());
+ }
+
// The orientation request from this DA may now be respected.
if (!ignoreOrientationRequest) {
return mDisplayContent.updateOrientation();
@@ -307,6 +314,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
return this;
}
+ /** Cheap way of doing cast and instanceof. */
+ DisplayArea.Tokens asTokens() {
+ return null;
+ }
+
@Override
void forAllDisplayAreas(Consumer<DisplayArea> callback) {
super.forAllDisplayAreas(callback);
@@ -544,6 +556,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
mLastOrientationSource = win;
return req;
}
+
+ @Override
+ final DisplayArea.Tokens asTokens() {
+ return this;
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 211bd5d9eb77..d4b319a525da 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -35,6 +35,7 @@ import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
import static com.android.server.wm.DisplayAreaPolicyBuilder.HierarchyBuilder;
import android.content.res.Resources;
+import android.os.Bundle;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -71,6 +72,10 @@ public abstract class DisplayAreaPolicy {
*/
public abstract void addWindow(WindowToken token);
+ /** Gets the {@link DisplayArea} which a {@link WindowToken} is about to be attached to. */
+ public abstract DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options,
+ boolean ownerCanManageAppTokens, boolean roundedCornerOverlay);
+
/**
* Gets the set of {@link DisplayArea} that are created for the given feature to apply to.
*/
@@ -86,7 +91,7 @@ public abstract class DisplayAreaPolicy {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
- DisplayArea<? extends WindowContainer> imeContainer) {
+ DisplayArea.Tokens imeContainer) {
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<TaskDisplayArea> tdaList = new ArrayList<>();
@@ -151,7 +156,7 @@ public abstract class DisplayAreaPolicy {
* @see DisplayAreaPolicy#DisplayAreaPolicy
*/
DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
- RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer);
+ RootDisplayArea root, DisplayArea.Tokens imeContainer);
/**
* Instantiates the device-specific {@link Provider}.
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 6a420874b924..c8fadf62eb2f 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -136,12 +137,12 @@ class DisplayAreaPolicyBuilder {
private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>();
/**
- * When a window is created, the policy will use this function to select the
- * {@link RootDisplayArea} to place that window in. The selected root can be either the one of
- * the {@link #mRootHierarchyBuilder} or the one of any of the
+ * When a window is created, the policy will use this function, which takes window type and
+ * options, to select the {@link RootDisplayArea} to place that window in. The selected root
+ * can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the
* {@link #mDisplayAreaGroupHierarchyBuilders}.
**/
- @Nullable private BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
+ @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
/** Defines the root hierarchy for the whole logical display. */
DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) {
@@ -161,37 +162,53 @@ class DisplayAreaPolicyBuilder {
/** The policy will use this function to find the root to place windows in. */
DisplayAreaPolicyBuilder setSelectRootForWindowFunc(
- BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc) {
+ BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) {
mSelectRootForWindowFunc = selectRootForWindowFunc;
return this;
}
- /** Makes sure the setting meets the requirement. */
+ /**
+ * Makes sure the setting meets the requirement:
+ * 1. {@link mRootHierarchyBuilder} must be set.
+ * 2. {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids.
+ * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids.
+ * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container.
+ * 5. There must be exactly one {@link HierarchyBuilder} that contains the default
+ * {@link TaskDisplayArea} with id {@link FEATURE_DEFAULT_TASK_CONTAINER}.
+ * 6. None of the ids is greater than {@link FEATURE_VENDOR_LAST}.
+ */
private void validate() {
if (mRootHierarchyBuilder == null) {
throw new IllegalStateException("Root must be set for the display area policy.");
}
- final Set<Integer> rootIdSet = new ArraySet<>();
- rootIdSet.add(mRootHierarchyBuilder.mRoot.mFeatureId);
+ final Set<Integer> uniqueIdSet = new ArraySet<>();
+ final Set<Integer> allIdSet = new ArraySet<>();
+ validateIds(mRootHierarchyBuilder, uniqueIdSet, allIdSet);
boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null;
boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder);
for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
- if (!rootIdSet.add(hierarchyBuilder.mRoot.mFeatureId)) {
- throw new IllegalStateException("There should not be two RootDisplayAreas with id "
- + hierarchyBuilder.mRoot.mFeatureId);
- }
+ validateIds(hierarchyBuilder, uniqueIdSet, allIdSet);
+
if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) {
throw new IllegalStateException(
"DisplayAreaGroup must contain at least one TaskDisplayArea.");
}
- containsImeContainer = containsImeContainer || hierarchyBuilder.mImeContainer != null;
+ if (containsImeContainer) {
+ if (hierarchyBuilder.mImeContainer != null) {
+ throw new IllegalStateException(
+ "Only one DisplayArea hierarchy can contain the IME container");
+ }
+ } else {
+ containsImeContainer = hierarchyBuilder.mImeContainer != null;
+ }
+
if (containsDefaultTda) {
if (containsDefaultTaskDisplayArea(hierarchyBuilder)) {
throw new IllegalStateException("Only one TaskDisplayArea can have the feature "
- + "of FEATURE_DEFAULT_TASK_CONTAINER");
+ + "id of FEATURE_DEFAULT_TASK_CONTAINER");
}
} else {
containsDefaultTda = containsDefaultTaskDisplayArea(hierarchyBuilder);
@@ -203,7 +220,8 @@ class DisplayAreaPolicyBuilder {
}
if (!containsDefaultTda) {
- throw new IllegalStateException("There must be a default TaskDisplayArea.");
+ throw new IllegalStateException("There must be a default TaskDisplayArea with id of "
+ + "FEATURE_DEFAULT_TASK_CONTAINER.");
}
}
@@ -218,6 +236,67 @@ class DisplayAreaPolicyBuilder {
return false;
}
+ /**
+ * Makes sure that ids meet requirement.
+ * {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids.
+ * {@link Feature} below the same {@link RootDisplayArea} must have unique ids, but
+ * {@link Feature} below different {@link RootDisplayArea} can have the same id so that we can
+ * organize them together.
+ * None of the ids is greater than {@link FEATURE_VENDOR_LAST}
+ *
+ * @param uniqueIdSet ids of {@link RootDisplayArea} and {@link TaskDisplayArea} that must be
+ * unique,
+ * @param allIdSet ids of {@link RootDisplayArea}, {@link TaskDisplayArea} and {@link Feature}.
+ */
+ private static void validateIds(HierarchyBuilder displayAreaHierarchy,
+ Set<Integer> uniqueIdSet, Set<Integer> allIdSet) {
+ // Root must have unique id.
+ final int rootId = displayAreaHierarchy.mRoot.mFeatureId;
+ if (!allIdSet.add(rootId) || !uniqueIdSet.add(rootId)) {
+ throw new IllegalStateException(
+ "RootDisplayArea must have unique id, but id=" + rootId + " is not unique.");
+ }
+ if (rootId > FEATURE_VENDOR_LAST) {
+ throw new IllegalStateException(
+ "RootDisplayArea should not have an id greater than FEATURE_VENDOR_LAST.");
+ }
+
+ // TDAs must have unique id.
+ for (int i = 0; i < displayAreaHierarchy.mTaskDisplayAreas.size(); i++) {
+ final int taskDisplayAreaId = displayAreaHierarchy.mTaskDisplayAreas.get(i).mFeatureId;
+ if (!allIdSet.add(taskDisplayAreaId) || !uniqueIdSet.add(taskDisplayAreaId)) {
+ throw new IllegalStateException("TaskDisplayArea must have unique id, but id="
+ + taskDisplayAreaId + " is not unique.");
+ }
+ if (taskDisplayAreaId > FEATURE_VENDOR_LAST) {
+ throw new IllegalStateException("TaskDisplayArea declared in the policy should not"
+ + "have an id greater than FEATURE_VENDOR_LAST.");
+ }
+ }
+
+ // Features below the same root must have unique ids.
+ final Set<Integer> featureIdSet = new ArraySet<>();
+ for (int i = 0; i < displayAreaHierarchy.mFeatures.size(); i++) {
+ final int featureId = displayAreaHierarchy.mFeatures.get(i).getId();
+ if (uniqueIdSet.contains(featureId)) {
+ throw new IllegalStateException("Feature must not have same id with any "
+ + "RootDisplayArea or TaskDisplayArea, but id=" + featureId + " is used");
+ }
+ if (!featureIdSet.add(featureId)) {
+ throw new IllegalStateException("Feature below the same root must have unique id, "
+ + "but id=" + featureId + " is not unique.");
+ }
+ if (featureId > FEATURE_VENDOR_LAST) {
+ throw new IllegalStateException(
+ "Feature should not have an id greater than FEATURE_VENDOR_LAST.");
+ }
+ }
+
+ // Features below different roots can have the same id so that we can organize them
+ // together.
+ allIdSet.addAll(featureIdSet);
+ }
+
Result build(WindowManagerService wmService) {
validate();
@@ -247,7 +326,7 @@ class DisplayAreaPolicyBuilder {
private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>();
private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
@Nullable
- private DisplayArea<? extends WindowContainer> mImeContainer;
+ private DisplayArea.Tokens mImeContainer;
HierarchyBuilder(RootDisplayArea root) {
mRoot = root;
@@ -270,7 +349,7 @@ class DisplayAreaPolicyBuilder {
}
/** Sets IME container as a child of this hierarchy root. */
- HierarchyBuilder setImeContainer(DisplayArea<? extends WindowContainer> imeContainer) {
+ HierarchyBuilder setImeContainer(DisplayArea.Tokens imeContainer) {
mImeContainer = imeContainer;
return this;
}
@@ -576,19 +655,19 @@ class DisplayAreaPolicyBuilder {
static class Result extends DisplayAreaPolicy {
final List<RootDisplayArea> mDisplayAreaGroupRoots;
- final BiFunction<WindowToken, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
+ final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;
private final TaskDisplayArea mDefaultTaskDisplayArea;
Result(WindowManagerService wmService, RootDisplayArea root,
List<RootDisplayArea> displayAreaGroupRoots,
- @Nullable BiFunction<WindowToken, Bundle, RootDisplayArea>
+ @Nullable BiFunction<Integer, Bundle, RootDisplayArea>
selectRootForWindowFunc) {
super(wmService, root);
mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
mSelectRootForWindowFunc = selectRootForWindowFunc == null
// Always return the highest level root of the logical display when the func is
// not specified.
- ? (window, options) -> mRoot
+ ? (type, options) -> mRoot
: selectRootForWindowFunc;
// Cache the default TaskDisplayArea for quick access.
@@ -610,7 +689,8 @@ class DisplayAreaPolicyBuilder {
@VisibleForTesting
DisplayArea.Tokens findAreaForToken(WindowToken token) {
- return mSelectRootForWindowFunc.apply(token, token.mOptions).findAreaForToken(token);
+ return mSelectRootForWindowFunc.apply(token.windowType, token.mOptions)
+ .findAreaForToken(token);
}
@VisibleForTesting
@@ -648,6 +728,13 @@ class DisplayAreaPolicyBuilder {
public TaskDisplayArea getDefaultTaskDisplayArea() {
return mDefaultTaskDisplayArea;
}
+
+ @Override
+ public DisplayArea.Tokens getDisplayAreaForWindowToken(int type, Bundle options,
+ boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
+ return mSelectRootForWindowFunc.apply(type, options).findAreaForToken(type,
+ ownerCanManageAppTokens, roundedCornerOverlay);
+ }
}
static class PendingArea {
@@ -706,6 +793,10 @@ class DisplayAreaPolicyBuilder {
private DisplayArea createArea(DisplayArea<DisplayArea> parent,
DisplayArea.Tokens[] areaForLayer) {
if (mExisting != null) {
+ if (mExisting.asTokens() != null) {
+ // Store the WindowToken container for layers
+ fillAreaForLayers(mExisting.asTokens(), areaForLayer);
+ }
return mExisting;
}
if (mSkipTokens) {
@@ -722,14 +813,18 @@ class DisplayAreaPolicyBuilder {
if (mFeature == null) {
final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
"Leaf:" + mMinLayer + ":" + mMaxLayer);
- for (int i = mMinLayer; i <= mMaxLayer; i++) {
- areaForLayer[i] = leaf;
- }
+ fillAreaForLayers(leaf, areaForLayer);
return leaf;
} else {
return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
}
}
+
+ private void fillAreaForLayers(DisplayArea.Tokens leaf, DisplayArea.Tokens[] areaForLayer) {
+ for (int i = mMinLayer; i <= mMaxLayer; i++) {
+ areaForLayer[i] = leaf;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1d2cd0a0a350..e88f8e390833 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -165,6 +165,7 @@ import android.graphics.Region.Op;
import android.hardware.display.DisplayManagerInternal;
import android.metrics.LogMaker;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -488,8 +489,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
ActivityRecord mFocusedApp = null;
- /** The last focused {@link TaskDisplayArea} on this display. */
- private TaskDisplayArea mLastFocusedTaskDisplayArea = null;
+ /**
+ * We only respect the orientation request from apps below this {@link TaskDisplayArea}.
+ * It is the last focused {@link TaskDisplayArea} on this display that handles orientation
+ * request.
+ */
+ @Nullable
+ private TaskDisplayArea mOrientationRequestingTaskDisplayArea = null;
/**
* The launching activity which is using fixed rotation transformation.
@@ -995,7 +1001,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
getPendingTransaction().apply();
// Setup the policy and build the display area hierarchy.
- mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
+ mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */, mImeWindowsContainers);
// Sets the display content for the children.
@@ -3325,7 +3331,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Called even if the focused app is not changed in case the app is moved to a different
// TaskDisplayArea.
- setLastFocusedTaskDisplayArea(newFocus.getDisplayArea());
+ onLastFocusedTaskDisplayAreaChanged(newFocus.getDisplayArea());
}
if (mFocusedApp == newFocus) {
return false;
@@ -3339,16 +3345,27 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/** Called when the focused {@link TaskDisplayArea} on this display may have changed. */
- @VisibleForTesting
- void setLastFocusedTaskDisplayArea(@Nullable TaskDisplayArea taskDisplayArea) {
- if (taskDisplayArea != null) {
- mLastFocusedTaskDisplayArea = taskDisplayArea;
+ void onLastFocusedTaskDisplayAreaChanged(@Nullable TaskDisplayArea taskDisplayArea) {
+ // Only record the TaskDisplayArea that handles orientation request.
+ if (taskDisplayArea != null && taskDisplayArea.handlesOrientationChangeFromDescendant()) {
+ mOrientationRequestingTaskDisplayArea = taskDisplayArea;
+ return;
+ }
+
+ // If the previous TDA no longer handles orientation request, clear it.
+ if (mOrientationRequestingTaskDisplayArea != null
+ && !mOrientationRequestingTaskDisplayArea
+ .handlesOrientationChangeFromDescendant()) {
+ mOrientationRequestingTaskDisplayArea = null;
}
}
- /** Gets the last focused {@link TaskDisplayArea} on this display. */
- TaskDisplayArea getLastFocusedTaskDisplayArea() {
- return mLastFocusedTaskDisplayArea;
+ /**
+ * Gets the {@link TaskDisplayArea} that we respect orientation requests from apps below it.
+ */
+ @Nullable
+ TaskDisplayArea getOrientationRequestingTaskDisplayArea() {
+ return mOrientationRequestingTaskDisplayArea;
}
/** Updates the layer assignment of windows on this display. */
@@ -4775,7 +4792,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return getWindowContainers().getSurfaceControl();
}
- @VisibleForTesting
DisplayArea.Tokens getImeContainer() {
return mImeWindowsContainers;
}
@@ -5683,6 +5699,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
public boolean getRequestedVisibility(@InternalInsetsType int type) {
+ if (type == ITYPE_IME) {
+ return getInsetsStateController().getImeSourceProvider().isImeShowing();
+ }
return mRequestedInsetsState.getSourceOrDefaultVisibility(type);
}
@@ -5728,4 +5747,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
MagnificationSpec getMagnificationSpec() {
return mMagnificationSpec;
}
+
+ DisplayArea getAreaForWindowToken(int windowType, Bundle options,
+ boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {
+ // TODO(b/159767464): figure out how to find an appropriate TDA.
+ if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
+ return getDefaultTaskDisplayArea();
+ }
+ // Return IME container here because it could be in one of sub RootDisplayAreas depending on
+ // the focused edit text. Also, the RootDisplayArea choosing strategy is implemented by
+ // the server side, but not mSelectRootForWindowFunc customized by OEM.
+ if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
+ return getImeContainer();
+ }
+ return mDisplayAreaPolicy.getDisplayAreaForWindowToken(windowType, options,
+ ownerCanManageAppToken, roundedCornerOverlay);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2ddd00101769..cd02e00c4383 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1427,7 +1427,7 @@ public class DisplayPolicy {
: (task != null ? task.getBounds() : null);
final InsetsState state =
mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
- computeWindowBounds(attrs, state, outFrame);
+ computeWindowBounds(attrs, state, windowToken, outFrame);
if (taskBounds != null) {
outFrame.intersect(taskBounds);
}
@@ -1714,11 +1714,11 @@ public class DisplayPolicy {
}
private void computeWindowBounds(WindowManager.LayoutParams attrs, InsetsState state,
- Rect outBounds) {
+ @Nullable WindowToken windowToken, Rect outBounds) {
final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
- final Rect dfu = state.getDisplayFrame();
+ final Rect df = windowToken != null ? windowToken.getBounds() : state.getDisplayFrame();
Insets insets = Insets.of(0, 0, 0, 0);
for (int i = types.size() - 1; i >= 0; i--) {
final InsetsSource source = state.peekSource(types.valueAt(i));
@@ -1726,13 +1726,13 @@ public class DisplayPolicy {
continue;
}
insets = Insets.max(insets, source.calculateInsets(
- dfu, attrs.isFitInsetsIgnoringVisibility()));
+ df, attrs.isFitInsetsIgnoringVisibility()));
}
final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0;
final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;
- outBounds.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom);
+ outBounds.set(df.left + left, df.top + top, df.right - right, df.bottom - bottom);
}
/**
@@ -1772,7 +1772,7 @@ public class DisplayPolicy {
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
final InsetsState state = win.getInsetsState();
- computeWindowBounds(attrs, state, df);
+ computeWindowBounds(attrs, state, win.mToken, df);
if (attached == null) {
pf.set(df);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
@@ -1845,7 +1845,6 @@ public class DisplayPolicy {
// They will later be cropped or shifted using the displayFrame in WindowState,
// which prevents overlap with the DisplayCutout.
if (!attachedInParent && !floatingInScreenWindow) {
- getRotatedWindowBounds(displayFrames, win, sTmpRect);
sTmpRect.set(pf);
pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf));
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index aa603aba0f78..57c947f572d9 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -64,7 +64,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
? "DisplayWindowSettingsProvider" : TAG_WM;
private static final String DATA_DISPLAY_SETTINGS_FILE_PATH = "system/display_settings.xml";
- private static final String VENDOR_DISPLAY_SETTINGS_PATH = "etc/display_settings.xml";
+ private static final String VENDOR_DISPLAY_SETTINGS_FILE_PATH = "etc/display_settings.xml";
private static final String WM_DISPLAY_COMMIT_TAG = "wm-displays";
private static final int IDENTIFIER_UNIQUE_ID = 0;
@@ -86,34 +86,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
void finishWrite(OutputStream os, boolean success);
}
- private final ReadableSettingsStorage mVendorSettingsStorage;
- /**
- * The preferred type of a display identifier to use when storing and retrieving entries from
- * the base (vendor) settings file.
- *
- * @see #getIdentifier(DisplayInfo, int)
- */
- @DisplayIdentifierType
- private int mVendorIdentifierType;
- private final Map<String, SettingsEntry> mVendorSettings = new HashMap<>();
-
- private final WritableSettingsStorage mOverrideSettingsStorage;
- /**
- * The preferred type of a display identifier to use when storing and retrieving entries from
- * the data (override) settings file.
- *
- * @see #getIdentifier(DisplayInfo, int)
- */
- @DisplayIdentifierType
- private int mOverrideIdentifierType;
- private final Map<String, SettingsEntry> mOverrideSettings = new HashMap<>();
-
- /**
- * Enables or disables settings provided from the vendor settings storage.
- *
- * @see #setVendorSettingsIgnored(boolean)
- */
- private boolean mIgnoreVendorSettings = true;
+ private ReadableSettings mBaseSettings;
+ private final WritableSettings mOverrideSettings;
DisplayWindowSettingsProvider() {
this(new AtomicFileStorage(getVendorSettingsFile()),
@@ -121,43 +95,48 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
}
@VisibleForTesting
- DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage vendorSettingsStorage,
+ DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage baseSettingsStorage,
@NonNull WritableSettingsStorage overrideSettingsStorage) {
- mVendorSettingsStorage = vendorSettingsStorage;
- mOverrideSettingsStorage = overrideSettingsStorage;
- readSettings();
+ mBaseSettings = new ReadableSettings(baseSettingsStorage);
+ mOverrideSettings = new WritableSettings(overrideSettingsStorage);
}
/**
- * Enables or disables settings provided from the vendor settings storage. If {@code true}, the
- * vendor settings will be ignored and only the override settings will be returned from
- * {@link #getSettings(DisplayInfo)}. If {@code false}, settings returned from
- * {@link #getSettings(DisplayInfo)} will be a merged result of the vendor settings and the
- * override settings.
+ * Overrides the path for the file that should be used to read base settings. If {@code null} is
+ * passed the default base settings file path will be used.
+ *
+ * @see #VENDOR_DISPLAY_SETTINGS_FILE_PATH
*/
- void setVendorSettingsIgnored(boolean ignored) {
- mIgnoreVendorSettings = ignored;
+ void setBaseSettingsFilePath(@Nullable String path) {
+ AtomicFile settingsFile;
+ if (path != null) {
+ settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG);
+ } else {
+ settingsFile = getVendorSettingsFile();
+ }
+
+ setBaseSettingsStorage(new AtomicFileStorage(settingsFile));
}
/**
- * Returns whether or not the vendor settings are being ignored.
+ * Overrides the storage that should be used to read base settings.
*
- * @see #setVendorSettingsIgnored(boolean)
+ * @see #setBaseSettingsFilePath(String)
*/
@VisibleForTesting
- boolean getVendorSettingsIgnored() {
- return mIgnoreVendorSettings;
+ void setBaseSettingsStorage(@NonNull ReadableSettingsStorage baseSettingsStorage) {
+ mBaseSettings = new ReadableSettings(baseSettingsStorage);
}
@Override
@NonNull
public SettingsEntry getSettings(@NonNull DisplayInfo info) {
- SettingsEntry vendorSettings = getVendorSettingsEntry(info);
- SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info);
- if (vendorSettings == null) {
+ SettingsEntry baseSettings = mBaseSettings.getSettingsEntry(info);
+ SettingsEntry overrideSettings = mOverrideSettings.getOrCreateSettingsEntry(info);
+ if (baseSettings == null) {
return new SettingsEntry(overrideSettings);
} else {
- SettingsEntry mergedSettings = new SettingsEntry(vendorSettings);
+ SettingsEntry mergedSettings = new SettingsEntry(baseSettings);
mergedSettings.updateFrom(overrideSettings);
return mergedSettings;
}
@@ -166,99 +145,126 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
@Override
@NonNull
public SettingsEntry getOverrideSettings(@NonNull DisplayInfo info) {
- return new SettingsEntry(getOrCreateOverrideSettingsEntry(info));
+ return new SettingsEntry(mOverrideSettings.getOrCreateSettingsEntry(info));
}
@Override
public void updateOverrideSettings(@NonNull DisplayInfo info,
@NonNull SettingsEntry overrides) {
- final SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info);
- boolean changed = overrideSettings.setTo(overrides);
- if (changed) {
- writeOverrideSettings();
- }
+ mOverrideSettings.updateSettingsEntry(info, overrides);
}
- @Nullable
- private SettingsEntry getVendorSettingsEntry(DisplayInfo info) {
- if (mIgnoreVendorSettings) {
+ /**
+ * Class that allows reading {@link SettingsEntry entries} from a
+ * {@link ReadableSettingsStorage}.
+ */
+ private static class ReadableSettings {
+ /**
+ * The preferred type of a display identifier to use when storing and retrieving entries
+ * from the settings entries.
+ *
+ * @see #getIdentifier(DisplayInfo)
+ */
+ @DisplayIdentifierType
+ protected int mIdentifierType;
+ protected final Map<String, SettingsEntry> mSettings = new HashMap<>();
+
+ ReadableSettings(ReadableSettingsStorage settingsStorage) {
+ loadSettings(settingsStorage);
+ }
+
+ @Nullable
+ final SettingsEntry getSettingsEntry(DisplayInfo info) {
+ final String identifier = getIdentifier(info);
+ SettingsEntry settings;
+ // Try to get corresponding settings using preferred identifier for the current config.
+ if ((settings = mSettings.get(identifier)) != null) {
+ return settings;
+ }
+ // Else, fall back to the display name.
+ if ((settings = mSettings.get(info.name)) != null) {
+ // Found an entry stored with old identifier.
+ mSettings.remove(info.name);
+ mSettings.put(identifier, settings);
+ return settings;
+ }
return null;
}
- final String identifier = getIdentifier(info, mVendorIdentifierType);
- SettingsEntry settings;
- // Try to get corresponding settings using preferred identifier for the current config.
- if ((settings = mVendorSettings.get(identifier)) != null) {
- return settings;
+ /** Gets the identifier of choice for the current config. */
+ protected final String getIdentifier(DisplayInfo displayInfo) {
+ if (mIdentifierType == IDENTIFIER_PORT && displayInfo.address != null) {
+ // Config suggests using port as identifier for physical displays.
+ if (displayInfo.address instanceof DisplayAddress.Physical) {
+ return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort();
+ }
+ }
+ return displayInfo.uniqueId;
}
- // Else, fall back to the display name.
- if ((settings = mVendorSettings.get(info.name)) != null) {
- // Found an entry stored with old identifier.
- mVendorSettings.remove(info.name);
- mVendorSettings.put(identifier, settings);
- return settings;
+
+ private void loadSettings(ReadableSettingsStorage settingsStorage) {
+ FileData fileData = readSettings(settingsStorage);
+ if (fileData != null) {
+ mIdentifierType = fileData.mIdentifierType;
+ mSettings.putAll(fileData.mSettings);
+ }
}
- return null;
}
- @NonNull
- private SettingsEntry getOrCreateOverrideSettingsEntry(DisplayInfo info) {
- final String identifier = getIdentifier(info, mOverrideIdentifierType);
- SettingsEntry settings;
- // Try to get corresponding settings using preferred identifier for the current config.
- if ((settings = mOverrideSettings.get(identifier)) != null) {
- return settings;
- }
- // Else, fall back to the display name.
- if ((settings = mOverrideSettings.get(info.name)) != null) {
- // Found an entry stored with old identifier.
- mOverrideSettings.remove(info.name);
- mOverrideSettings.put(identifier, settings);
- writeOverrideSettings();
- return settings;
+ /**
+ * Class that allows reading {@link SettingsEntry entries} from, and writing entries to, a
+ * {@link WritableSettingsStorage}.
+ */
+ private static final class WritableSettings extends ReadableSettings {
+ private final WritableSettingsStorage mSettingsStorage;
+
+ WritableSettings(WritableSettingsStorage settingsStorage) {
+ super(settingsStorage);
+ mSettingsStorage = settingsStorage;
}
- settings = new SettingsEntry();
- mOverrideSettings.put(identifier, settings);
- return settings;
- }
+ @NonNull
+ SettingsEntry getOrCreateSettingsEntry(DisplayInfo info) {
+ final String identifier = getIdentifier(info);
+ SettingsEntry settings;
+ // Try to get corresponding settings using preferred identifier for the current config.
+ if ((settings = mSettings.get(identifier)) != null) {
+ return settings;
+ }
+ // Else, fall back to the display name.
+ if ((settings = mSettings.get(info.name)) != null) {
+ // Found an entry stored with old identifier.
+ mSettings.remove(info.name);
+ mSettings.put(identifier, settings);
+ writeSettings();
+ return settings;
+ }
- private void readSettings() {
- FileData vendorFileData = readSettings(mVendorSettingsStorage);
- if (vendorFileData != null) {
- mVendorIdentifierType = vendorFileData.mIdentifierType;
- mVendorSettings.putAll(vendorFileData.mSettings);
+ settings = new SettingsEntry();
+ mSettings.put(identifier, settings);
+ return settings;
}
- FileData overrideFileData = readSettings(mOverrideSettingsStorage);
- if (overrideFileData != null) {
- mOverrideIdentifierType = overrideFileData.mIdentifierType;
- mOverrideSettings.putAll(overrideFileData.mSettings);
+ void updateSettingsEntry(DisplayInfo info, SettingsEntry settings) {
+ final SettingsEntry overrideSettings = getOrCreateSettingsEntry(info);
+ final boolean changed = overrideSettings.setTo(settings);
+ if (changed) {
+ writeSettings();
+ }
}
- }
-
- private void writeOverrideSettings() {
- FileData fileData = new FileData();
- fileData.mIdentifierType = mOverrideIdentifierType;
- fileData.mSettings.putAll(mOverrideSettings);
- writeSettings(mOverrideSettingsStorage, fileData);
- }
- /** Gets the identifier of choice for the current config. */
- private static String getIdentifier(DisplayInfo displayInfo, @DisplayIdentifierType int type) {
- if (type == IDENTIFIER_PORT && displayInfo.address != null) {
- // Config suggests using port as identifier for physical displays.
- if (displayInfo.address instanceof DisplayAddress.Physical) {
- return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort();
- }
+ private void writeSettings() {
+ FileData fileData = new FileData();
+ fileData.mIdentifierType = mIdentifierType;
+ fileData.mSettings.putAll(mSettings);
+ DisplayWindowSettingsProvider.writeSettings(mSettingsStorage, fileData);
}
- return displayInfo.uniqueId;
}
@NonNull
private static AtomicFile getVendorSettingsFile() {
final File vendorFile = new File(Environment.getVendorDirectory(),
- VENDOR_DISPLAY_SETTINGS_PATH);
+ VENDOR_DISPLAY_SETTINGS_FILE_PATH);
return new AtomicFile(vendorFile, WM_DISPLAY_COMMIT_TAG);
}
@@ -444,12 +450,12 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
out.attributeInt(null, "windowingMode", settingsEntry.mWindowingMode);
}
if (settingsEntry.mUserRotationMode != null) {
- out.attribute(null, "userRotationMode",
- settingsEntry.mUserRotationMode.toString());
+ out.attributeInt(null, "userRotationMode",
+ settingsEntry.mUserRotationMode);
}
if (settingsEntry.mUserRotation != null) {
- out.attribute(null, "userRotation",
- settingsEntry.mUserRotation.toString());
+ out.attributeInt(null, "userRotation",
+ settingsEntry.mUserRotation);
}
if (settingsEntry.mForcedWidth != 0 && settingsEntry.mForcedHeight != 0) {
out.attributeInt(null, "forcedWidth", settingsEntry.mForcedWidth);
@@ -459,30 +465,30 @@ class DisplayWindowSettingsProvider implements SettingsProvider {
out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity);
}
if (settingsEntry.mForcedScalingMode != null) {
- out.attribute(null, "forcedScalingMode",
- settingsEntry.mForcedScalingMode.toString());
+ out.attributeInt(null, "forcedScalingMode",
+ settingsEntry.mForcedScalingMode);
}
if (settingsEntry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) {
out.attributeInt(null, "removeContentMode", settingsEntry.mRemoveContentMode);
}
if (settingsEntry.mShouldShowWithInsecureKeyguard != null) {
- out.attribute(null, "shouldShowWithInsecureKeyguard",
- settingsEntry.mShouldShowWithInsecureKeyguard.toString());
+ out.attributeBoolean(null, "shouldShowWithInsecureKeyguard",
+ settingsEntry.mShouldShowWithInsecureKeyguard);
}
if (settingsEntry.mShouldShowSystemDecors != null) {
- out.attribute(null, "shouldShowSystemDecors",
- settingsEntry.mShouldShowSystemDecors.toString());
+ out.attributeBoolean(null, "shouldShowSystemDecors",
+ settingsEntry.mShouldShowSystemDecors);
}
if (settingsEntry.mImePolicy != null) {
out.attributeInt(null, "imePolicy", settingsEntry.mImePolicy);
}
if (settingsEntry.mFixedToUserRotation != null) {
- out.attribute(null, "fixedToUserRotation",
- settingsEntry.mFixedToUserRotation.toString());
+ out.attributeInt(null, "fixedToUserRotation",
+ settingsEntry.mFixedToUserRotation);
}
if (settingsEntry.mIgnoreOrientationRequest != null) {
- out.attribute(null, "ignoreOrientationRequest",
- settingsEntry.mIgnoreOrientationRequest.toString());
+ out.attributeBoolean(null, "ignoreOrientationRequest",
+ settingsEntry.mIgnoreOrientationRequest);
}
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 14880ed30f24..9602880486b5 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.app.ActivityTaskManager;
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.net.Uri;
@@ -33,6 +32,7 @@ import java.util.ArrayList;
class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
implements IBinder.DeathRecipient {
+ private final WindowManagerGlobalLock mGlobalLock;
private final int mSourceUid;
private final String mTargetPackage;
private final int mMode;
@@ -45,8 +45,9 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
private IBinder mPermissionOwnerToken = null;
private IBinder mTransientToken = null;
- DragAndDropPermissionsHandler(ClipData clipData, int sourceUid, String targetPackage, int mode,
- int sourceUserId, int targetUserId) {
+ DragAndDropPermissionsHandler(WindowManagerGlobalLock lock, ClipData clipData, int sourceUid,
+ String targetPackage, int mode, int sourceUserId, int targetUserId) {
+ mGlobalLock = lock;
mSourceUid = sourceUid;
mTargetPackage = targetPackage;
mMode = mode;
@@ -64,8 +65,7 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
mActivityToken = activityToken;
// Will throw if Activity is not found.
- IBinder permissionOwner = ActivityTaskManager.getService().
- getUriPermissionOwnerForActivity(mActivityToken);
+ IBinder permissionOwner = getUriPermissionOwnerForActivity(mActivityToken);
doTake(permissionOwner);
}
@@ -105,8 +105,7 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
IBinder permissionOwner = null;
if (mActivityToken != null) {
try {
- permissionOwner = ActivityTaskManager.getService().
- getUriPermissionOwnerForActivity(mActivityToken);
+ permissionOwner = getUriPermissionOwnerForActivity(mActivityToken);
} catch (Exception e) {
// Activity is destroyed, permissions already revoked.
return;
@@ -126,6 +125,18 @@ class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
}
}
+ private IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
+ ActivityTaskManagerService.enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+ synchronized (mGlobalLock) {
+ ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+ if (r == null) {
+ throw new IllegalArgumentException("Activity does not exist; token="
+ + activityToken);
+ }
+ return r.getUriPermissionsLocked().getExternalToken();
+ }
+ }
+
@Override
public void binderDied() {
try {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index ad4e64a08183..86518ea4ccc1 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -59,7 +59,6 @@ import android.view.WindowManager;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import com.android.internal.os.SomeArgs;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
@@ -590,7 +589,7 @@ class DragState {
final DragAndDropPermissionsHandler dragAndDropPermissions;
if ((mFlags & View.DRAG_FLAG_GLOBAL) != 0 && (mFlags & DRAG_FLAGS_URI_ACCESS) != 0
&& mData != null) {
- dragAndDropPermissions = new DragAndDropPermissionsHandler(
+ dragAndDropPermissions = new DragAndDropPermissionsHandler(mService.mGlobalLock,
mData,
mUid,
touchedWin.getOwningPackage(),
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 818d96ceb5a6..91106eff9805 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -42,6 +42,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
private InsetsControlTarget mImeTargetFromIme;
private Runnable mShowImeRunner;
private boolean mIsImeLayoutDrawn;
+ private boolean mImeShowing;
ImeInsetsSourceProvider(InsetsSource source,
InsetsStateController stateController, DisplayContent displayContent) {
@@ -81,6 +82,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
target.getWindow() != null ? target.getWindow().getName() : "");
+ setImeShowing(true);
target.showInsets(WindowInsets.Type.ime(), true /* fromIme */);
Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
if (target != mImeTargetFromIme && mImeTargetFromIme != null) {
@@ -155,12 +157,14 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
@Override
public void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
+ pw.print(prefix);
+ pw.print("mImeShowing=");
+ pw.print(mImeShowing);
if (mImeTargetFromIme != null) {
- pw.print(prefix);
- pw.print("showImePostLayout pending for mImeTargetFromIme=");
+ pw.print(" showImePostLayout pending for mImeTargetFromIme=");
pw.print(mImeTargetFromIme);
- pw.println();
}
+ pw.println();
}
@Override
@@ -173,4 +177,20 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn);
proto.end(token);
}
+
+ /**
+ * Sets whether the IME is currently supposed to be showing according to
+ * InputMethodManagerService.
+ */
+ public void setImeShowing(boolean imeShowing) {
+ mImeShowing = imeShowing;
+ }
+
+ /**
+ * Returns whether the IME is currently supposed to be showing according to
+ * InputMethodManagerService.
+ */
+ public boolean isImeShowing() {
+ return mImeShowing;
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 0605ed800565..28a99825d0b4 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -328,7 +328,7 @@ class InsetsSourceProvider {
final Point surfacePosition = getWindowFrameSurfacePosition();
mAdapter = new ControlAdapter(surfacePosition);
if (getSource().getType() == ITYPE_IME) {
- setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
+ setClientVisible(target.getRequestedVisibility(mSource.getType()));
}
final Transaction t = mDisplayContent.getPendingTransaction();
mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 52ada472f0aa..fd42b2467d19 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -29,7 +29,6 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
import android.view.DisplayInfo;
-import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
import java.io.PrintWriter;
@@ -63,8 +62,6 @@ class PinnedStackController {
private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler =
new PinnedStackListenerDeathHandler();
- private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback();
-
/** Whether the PiP is entering or leaving. */
private boolean mIsPipWindowingModeChanging;
@@ -86,18 +83,6 @@ class PinnedStackController {
private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
/**
- * The callback object passed to listeners for them to notify the controller of state changes.
- */
- private class PinnedStackControllerCallback extends IPinnedStackController.Stub {
- @Override
- public int getDisplayRotation() {
- synchronized (mService.mGlobalLock) {
- return mDisplayInfo.rotation;
- }
- }
- }
-
- /**
* Handler for the case where the listener dies.
*/
private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient {
@@ -141,7 +126,6 @@ class PinnedStackController {
void registerPinnedStackListener(IPinnedStackListener listener) {
try {
listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0);
- listener.onListenerRegistered(mCallbacks);
mPinnedStackListener = listener;
notifyDisplayInfoChanged(mDisplayInfo);
notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
index da04f438a496..393055eab111 100644
--- a/services/core/java/com/android/server/wm/RootDisplayArea.java
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -16,11 +16,17 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+import android.annotation.Nullable;
+
+import com.android.server.policy.WindowManagerPolicy;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -72,7 +78,8 @@ class RootDisplayArea extends DisplayArea<DisplayArea> {
* match the root.
*/
void placeImeContainer(DisplayArea.Tokens imeContainer) {
- if (imeContainer.getRootDisplayArea() == this) {
+ final RootDisplayArea previousRoot = imeContainer.getRootDisplayArea();
+ if (previousRoot == this) {
// No need to reparent if IME container is below the same root.
return;
}
@@ -88,7 +95,9 @@ class RootDisplayArea extends DisplayArea<DisplayArea> {
+ "FEATURE_IME_PLACEHOLDER");
}
+ previousRoot.updateImeContainerForLayers(null /* imeContainer */);
imeContainer.reparent(imeDisplayAreas.get(0), POSITION_TOP);
+ updateImeContainerForLayers(imeContainer);
return;
}
}
@@ -97,12 +106,23 @@ class RootDisplayArea extends DisplayArea<DisplayArea> {
}
/** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */
+ @Nullable
DisplayArea.Tokens findAreaForToken(WindowToken token) {
- int windowLayerFromType = token.getWindowLayerFromType();
+ return findAreaForToken(token.windowType, token.mOwnerCanManageAppTokens,
+ token.mRoundedCornerOverlay);
+ }
+
+ @Nullable
+ DisplayArea.Tokens findAreaForToken(int windowType, boolean ownerCanManageAppTokens,
+ boolean roundedCornerOverlay) {
+ // TODO(b/159767464): cover TYPE_INPUT_METHOD(_DIALOG) case here. mAreaForLayer doesn't
+ // contain IME container.
+ int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
+ ownerCanManageAppTokens);
if (windowLayerFromType == APPLICATION_LAYER) {
throw new IllegalArgumentException(
"There shouldn't be WindowToken on APPLICATION_LAYER");
- } else if (token.mRoundedCornerOverlay) {
+ } else if (roundedCornerOverlay) {
windowLayerFromType = mAreaForLayer.length - 1;
}
return mAreaForLayer[windowLayerFromType];
@@ -119,4 +139,10 @@ class RootDisplayArea extends DisplayArea<DisplayArea> {
mAreaForLayer = areaForLayer;
mFeatureToDisplayAreas = featureToDisplayAreas;
}
+
+ private void updateImeContainerForLayers(@Nullable DisplayArea.Tokens imeContainer) {
+ final WindowManagerPolicy policy = mWmService.mPolicy;
+ mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)] = imeContainer;
+ mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)] = imeContainer;
+ }
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 7df2b407557d..6e0efbf8bd51 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -26,7 +26,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.util.DebugUtils;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
@@ -388,7 +387,7 @@ class SurfaceAnimator {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
- .setName(surface + " - animation-leash")
+ .setName(surface + " - animation-leash of " + animationTypeToString(type))
// TODO(b/151665759) Defer reparent calls
// We want the leash to be visible immediately because the transaction which shows
// the leash may be deferred but the reparent will not. This will cause the leashed
@@ -430,8 +429,7 @@ class SurfaceAnimator {
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
- pw.print(" mAnimationType=" + DebugUtils.valueToString(SurfaceAnimator.class,
- "ANIMATION_TYPE_", mAnimationType));
+ pw.print(" mAnimationType=" + animationTypeToString(mAnimationType));
pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : "");
pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation);
if (mAnimation != null) {
@@ -513,6 +511,23 @@ class SurfaceAnimator {
@interface AnimationType {}
/**
+ * Converts {@link AnimationType} to String.
+ */
+ private static String animationTypeToString(@AnimationType int type) {
+ switch (type) {
+ case ANIMATION_TYPE_NONE: return "none";
+ case ANIMATION_TYPE_APP_TRANSITION: return "app_transition";
+ case ANIMATION_TYPE_SCREEN_ROTATION: return "screen_rotation";
+ case ANIMATION_TYPE_DIMMER: return "dimmer";
+ case ANIMATION_TYPE_RECENTS: return "recents_animation";
+ case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation";
+ case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation";
+ case ANIMATION_TYPE_FIXED_TRANSFORM: return "fixed_rotation";
+ default: return "unknown type:" + type;
+ }
+ }
+
+ /**
* Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
* component that is running the animation when the animation is finished.
*/
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4498a8c82583..9425602763c5 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -641,9 +641,7 @@ final class TaskDisplayArea extends DisplayArea<Task> {
@Override
int getOrientation(int candidate) {
mLastOrientationSource = null;
- // Only allow to specify orientation if this TDA is not set to ignore orientation request,
- // and it has the focus.
- if (mIgnoreOrientationRequest || !isLastFocused()) {
+ if (!canSpecifyOrientation()) {
return SCREEN_ORIENTATION_UNSET;
}
@@ -1918,10 +1916,14 @@ final class TaskDisplayArea extends DisplayArea<Task> {
return lastReparentedStack;
}
- /** Whether this task display area is the last focused one on this logical display. */
+ /** Whether this task display area can request orientation. */
@VisibleForTesting
- boolean isLastFocused() {
- return mDisplayContent.getLastFocusedTaskDisplayArea() == this;
+ boolean canSpecifyOrientation() {
+ // Only allow to specify orientation if this TDA is not set to ignore orientation request,
+ // and it is the last focused one on this logical display that can request orientation
+ // request.
+ return !mIgnoreOrientationRequest
+ && mDisplayContent.getOrientationRequestingTaskDisplayArea() == this;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index cf6468d66b57..adc7a227861a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -98,6 +98,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
+import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -314,6 +315,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
final SurfaceControl.Transaction mSyncTransaction;
@SyncState int mSyncState = SYNC_STATE_NONE;
+ private final List<WindowContainerListener> mListeners = new ArrayList<>();
+
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
@@ -637,6 +640,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (mParent != null) {
mParent.removeChild(this);
}
+
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onRemoved();
+ }
}
/**
@@ -819,6 +826,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
final WindowContainer child = mChildren.get(i);
child.onDisplayChanged(dc);
}
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onDisplayChanged(dc);
+ }
}
DisplayContent getDisplayContent() {
@@ -1152,10 +1162,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
/**
* Check if this container or its parent will handle orientation changes from descendants. It's
- * different from the return value of {@link #onDescendantOrientationChanged(IBinder,
- * WindowContainer)} in the sense that the return value of this method tells if this
- * container or its parent will handle the request eventually, while the return value of the
- * other method is if it handled the request synchronously.
+ * different from the return value of {@link #onDescendantOrientationChanged(WindowContainer)}
+ * in the sense that the return value of this method tells if this container or its parent will
+ * handle the request eventually, while the return value of the other method is if it handled
+ * the request synchronously.
*
* @return {@code true} if it handles or will handle orientation change in the future; {@code
* false} if it won't handle the change at anytime.
@@ -1221,7 +1231,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
- * Calls {@link #setOrientation(int, IBinder, WindowContainer)} with {@code null} to the last 2
+ * Calls {@link #setOrientation(int, WindowContainer)} with {@code null} to the last 2
* parameters.
*
* @param orientation the specified orientation.
@@ -3106,4 +3116,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mSyncState = SYNC_STATE_NONE;
prepareSync();
}
+
+ void registerWindowContainerListener(WindowContainerListener listener) {
+ if (mListeners.contains(listener)) {
+ return;
+ }
+ mListeners.add(listener);
+ // Also register to ConfigurationChangeListener to receive configuration changes.
+ registerConfigurationChangeListener(listener);
+ listener.onDisplayChanged(getDisplayContent());
+ }
+
+ void unregisterWindowContainerListener(WindowContainerListener listener) {
+ mListeners.remove(listener);
+ unregisterConfigurationChangeListener(listener);
+ }
}
diff --git a/cmds/statsd/src/experiment_ids.proto b/services/core/java/com/android/server/wm/WindowContainerListener.java
index c2036314cf58..ac1fe173dd09 100644
--- a/cmds/statsd/src/experiment_ids.proto
+++ b/services/core/java/com/android/server/wm/WindowContainerListener.java
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-syntax = "proto2";
+package com.android.server.wm;
-package android.os.statsd;
+/**
+ * Interface for listening to changes in a {@link WindowContainer}. A usage of this listener is
+ * to receive the changes and propagate them to the client side.
+ */
+interface WindowContainerListener extends ConfigurationContainerListener {
-option java_package = "com.android.internal.os";
-option java_outer_classname = "ExperimentIdsProto";
+ /** @see WindowContainer#onDisplayChanged(DisplayContent) */
+ default void onDisplayChanged(DisplayContent dc) {}
-// StatsLogProcessor uses the proto to parse experiment ids from
-// BinaryPushStateChanged atoms. This needs to be in sync with
-// TrainExperimentIds in atoms.proto.
-message ExperimentIds {
- repeated int64 experiment_id = 1;
+ /** Called when {@link WindowContainer#removeImmediately()} is invoked. */
+ default void onRemoved() {}
}
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
new file mode 100644
index 000000000000..051ece38b26f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
+
+import android.annotation.NonNull;
+import android.app.IWindowToken;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A controller to register/unregister {@link WindowContainerListener} for
+ * {@link android.app.WindowContext}.
+ *
+ * <ul>
+ * <li>When a {@link android.app.WindowContext} is created, it registers the listener via
+ * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)}
+ * automatically.</li>
+ * <li>When the {@link android.app.WindowContext} adds the first window to the screen via
+ * {@link android.view.WindowManager#addView(View, ViewGroup.LayoutParams)},
+ * {@link WindowManagerService} then updates the {@link WindowContextListenerImpl} to listen
+ * to corresponding {@link WindowToken} via this controller.</li>
+ * <li>When the {@link android.app.WindowContext} is GCed, it unregisters the previously
+ * registered listener via
+ * {@link WindowManagerService#unregisterWindowContextListener(IBinder)}.
+ * {@link WindowManagerService} is also responsible for removing the
+ * {@link android.app.WindowContext} created {@link WindowToken}.</li>
+ * </ul>
+ * <p>Note that the listener may be removed earlier than the
+ * {@link #unregisterWindowContainerListener(IBinder)} if the listened {@link WindowContainer} was
+ * removed. An example is that the {@link DisplayArea} is removed when users unfold the
+ * foldable devices. Another example is that the associated external display is detached.</p>
+ */
+class WindowContextListenerController {
+ @VisibleForTesting
+ final Map<IBinder, WindowContextListenerImpl> mListeners = new ArrayMap<>();
+
+ /**
+ * Registers the listener to a {@code container} which is associated with
+ * a {@code clientToken}, which is a {@link android.app.WindowContext} representation. If the
+ * listener associated with {@code clientToken} hasn't been initialized yet, create one
+ * {@link WindowContextListenerImpl}. Otherwise, the listener associated with
+ * {@code clientToken} switches to listen to the {@code container}.
+ *
+ * @param clientToken the token to associate with the listener
+ * @param container the {@link WindowContainer} which the listener is going to listen to.
+ * @param ownerUid the caller UID
+ */
+ void registerWindowContainerListener(@NonNull IBinder clientToken,
+ @NonNull WindowContainer container, int ownerUid) {
+ WindowContextListenerImpl listener = mListeners.get(clientToken);
+ if (listener == null) {
+ listener = new WindowContextListenerImpl(clientToken, container, ownerUid);
+ listener.register();
+ } else {
+ listener.updateContainer(container);
+ }
+ }
+
+ void unregisterWindowContainerListener(IBinder clientToken) {
+ final WindowContextListenerImpl listener = mListeners.get(clientToken);
+ // Listeners may be removed earlier. An example is the display where the listener is
+ // located is detached. In this case, all window containers on the display, as well as
+ // their listeners will be removed before their listeners are unregistered.
+ if (listener == null) {
+ return;
+ }
+ listener.unregister();
+ }
+
+ boolean assertCallerCanRemoveListener(IBinder clientToken, boolean callerCanManageAppTokens,
+ int callingUid) {
+ final WindowContextListenerImpl listener = mListeners.get(clientToken);
+ if (listener == null) {
+ ProtoLog.i(WM_DEBUG_ADD_REMOVE, "The listener does not exist.");
+ return false;
+ }
+ if (callerCanManageAppTokens) {
+ return true;
+ }
+ if (callingUid != listener.mOwnerUid) {
+ throw new UnsupportedOperationException("Uid mismatch. Caller uid is " + callingUid
+ + ", while the listener's owner is from " + listener.mOwnerUid);
+ }
+ return true;
+ }
+
+ @VisibleForTesting
+ class WindowContextListenerImpl implements WindowContainerListener {
+ @NonNull private final IBinder mClientToken;
+ private final int mOwnerUid;
+ @NonNull private WindowContainer mContainer;
+
+ private DeathRecipient mDeathRecipient;
+
+ private int mLastReportedDisplay = INVALID_DISPLAY;
+ private Configuration mLastReportedConfig;
+
+ private WindowContextListenerImpl(IBinder clientToken, WindowContainer container,
+ int ownerUid) {
+ mClientToken = clientToken;
+ mContainer = Objects.requireNonNull(container);
+ mOwnerUid = ownerUid;
+
+ final DeathRecipient deathRecipient = new DeathRecipient();
+ try {
+ deathRecipient.linkToDeath();
+ mDeathRecipient = deathRecipient;
+ } catch (RemoteException e) {
+ ProtoLog.e(WM_ERROR, "Could not register window container listener token=%s, "
+ + "container=%s", mClientToken, mContainer);
+ }
+ }
+
+ /** TEST ONLY: returns the {@link WindowContainer} of the listener */
+ @VisibleForTesting
+ WindowContainer getWindowContainer() {
+ return mContainer;
+ }
+
+ private void updateContainer(WindowContainer newContainer) {
+ if (mContainer.equals(newContainer)) {
+ return;
+ }
+ mContainer.unregisterWindowContainerListener(this);
+ mContainer = newContainer;
+ clear();
+ register();
+ }
+
+ private void register() {
+ if (mDeathRecipient == null) {
+ throw new IllegalStateException("Invalid client token: " + mClientToken);
+ }
+ mListeners.putIfAbsent(mClientToken, this);
+ mContainer.registerWindowContainerListener(this);
+ reportConfigToWindowTokenClient();
+ }
+
+ private void unregister() {
+ mContainer.unregisterWindowContainerListener(this);
+ mListeners.remove(mClientToken);
+ }
+
+ private void clear() {
+ mLastReportedConfig = null;
+ mLastReportedDisplay = INVALID_DISPLAY;
+ }
+
+ @Override
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ reportConfigToWindowTokenClient();
+ }
+
+ @Override
+ public void onDisplayChanged(DisplayContent dc) {
+ reportConfigToWindowTokenClient();
+ }
+
+ private void reportConfigToWindowTokenClient() {
+ if (mDeathRecipient == null) {
+ throw new IllegalStateException("Invalid client token: " + mClientToken);
+ }
+
+ if (mLastReportedConfig == null) {
+ mLastReportedConfig = new Configuration();
+ }
+ final Configuration config = mContainer.getConfiguration();
+ final int displayId = mContainer.getDisplayContent().getDisplayId();
+ if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
+ // No changes since last reported time.
+ return;
+ }
+
+ mLastReportedConfig.setTo(config);
+ mLastReportedDisplay = displayId;
+
+ IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
+ try {
+ windowTokenClient.onConfigurationChanged(config, displayId);
+ } catch (RemoteException e) {
+ ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
+ }
+ }
+
+ @Override
+ public void onRemoved() {
+ if (mDeathRecipient == null) {
+ throw new IllegalStateException("Invalid client token: " + mClientToken);
+ }
+ mDeathRecipient.unlinkToDeath();
+ IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(mClientToken);
+ try {
+ windowTokenClient.onWindowTokenRemoved();
+ } catch (RemoteException e) {
+ ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
+ }
+ unregister();
+ }
+
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ synchronized (mContainer.mWmService.mGlobalLock) {
+ mDeathRecipient = null;
+ unregister();
+ }
+ }
+
+ void linkToDeath() throws RemoteException {
+ mClientToken.linkToDeath(this, 0);
+ }
+
+ void unlinkToDeath() {
+ mClientToken.unlinkToDeath(this, 0);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f8b5914d3e53..8fd342c25bc3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -44,8 +44,8 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
-import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS;
import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
+import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
@@ -459,7 +459,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowTracing mWindowTracing;
- final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;
+ private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;
final private KeyguardDisableHandler mKeyguardDisableHandler;
// TODO: eventually unify all keyguard state in a common place instead of having it spread over
@@ -769,6 +769,8 @@ public class WindowManagerService extends IWindowManager.Stub
final AnrController mAnrController;
private final ImpressionAttestationController mImpressionAttestationController;
+ private final WindowContextListenerController mWindowContextListenerController =
+ new WindowContextListenerController();
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
@@ -796,8 +798,8 @@ public class WindowManagerService extends IWindowManager.Stub
DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM);
private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor(
DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR);
- private final Uri mIgnoreVendorDisplaySettingsUri = Settings.Global.getUriFor(
- DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS);
+ private final Uri mDisplaySettingsPathUri = Settings.Global.getUriFor(
+ DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
public SettingsObserver() {
super(new Handler());
@@ -822,7 +824,7 @@ public class WindowManagerService extends IWindowManager.Stub
UserHandle.USER_ALL);
resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this,
UserHandle.USER_ALL);
- resolver.registerContentObserver(mIgnoreVendorDisplaySettingsUri, false, this,
+ resolver.registerContentObserver(mDisplaySettingsPathUri, false, this,
UserHandle.USER_ALL);
}
@@ -867,8 +869,8 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (mIgnoreVendorDisplaySettingsUri.equals(uri)) {
- updateIgnoreVendorDisplaySettings();
+ if (mDisplaySettingsPathUri.equals(uri)) {
+ updateDisplaySettingsLocation();
return;
}
@@ -962,12 +964,12 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmService.mSizeCompatFreeform = sizeCompatFreeform;
}
- void updateIgnoreVendorDisplaySettings() {
+ void updateDisplaySettingsLocation() {
final ContentResolver resolver = mContext.getContentResolver();
- final boolean ignoreVendorSettings = Settings.Global.getInt(resolver,
- DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0;
+ final String filePath = Settings.Global.getString(resolver,
+ DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
synchronized (mGlobalLock) {
- mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorSettings);
+ mDisplayWindowSettingsProvider.setBaseSettingsFilePath(filePath);
mRoot.forAllDisplays(display -> {
mDisplayWindowSettings.applySettingsToDisplayLocked(display);
display.reconfigureDisplayLocked();
@@ -1121,10 +1123,7 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean isRecentsAnimationTarget = getRecentsAnimationController() != null
&& getRecentsAnimationController().isTargetApp(atoken);
if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget) {
- try {
- mActivityTaskManager.notifyLaunchTaskBehindComplete(atoken.token);
- } catch (RemoteException e) {
- }
+ mAtmService.mTaskSupervisor.scheduleLaunchTaskBehindComplete(atoken.token);
atoken.mLaunchTaskBehind = false;
} else {
atoken.updateReportedVisibilityLocked();
@@ -1132,9 +1131,11 @@ public class WindowManagerService extends IWindowManager.Stub
// successfully finishes.
if (atoken.mEnteringAnimation && !isRecentsAnimationTarget) {
atoken.mEnteringAnimation = false;
- try {
- mActivityTaskManager.notifyEnterAnimationComplete(atoken.token);
- } catch (RemoteException e) {
+ if (atoken != null && atoken.attachedToProcess()) {
+ try {
+ atoken.app.getThread().scheduleEnterAnimationComplete(atoken.appToken);
+ } catch (RemoteException e) {
+ }
}
}
}
@@ -1330,10 +1331,12 @@ public class WindowManagerService extends IWindowManager.Stub
mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver,
DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;
- final boolean ignoreVendorDisplaySettings = Settings.Global.getInt(resolver,
- DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0;
+ final String displaySettingsPath = Settings.Global.getString(resolver,
+ DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
mDisplayWindowSettingsProvider = new DisplayWindowSettingsProvider();
- mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorDisplaySettings);
+ if (displaySettingsPath != null) {
+ mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath);
+ }
mDisplayWindowSettings = new DisplayWindowSettings(this, mDisplayWindowSettingsProvider);
IntentFilter filter = new IntentFilter();
@@ -1376,6 +1379,10 @@ public class WindowManagerService extends IWindowManager.Stub
mStartingSurfaceController = new StartingSurfaceController(this);
}
+ DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
+ return mDisplayAreaPolicyProvider;
+ }
+
private void setGlobalShadowSettings() {
final TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
float lightY = a.getDimension(R.styleable.Lighting_lightY, 0);
@@ -2622,6 +2629,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean checkCallingPermission(String permission, String func) {
+ return checkCallingPermission(permission, func, true /* printLog */);
+ }
+
+ boolean checkCallingPermission(String permission, String func, boolean printLog) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == myPid()) {
return true;
@@ -2631,8 +2642,10 @@ public class WindowManagerService extends IWindowManager.Stub
== PackageManager.PERMISSION_GRANTED) {
return true;
}
- ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s",
- func, Binder.getCallingPid(), Binder.getCallingUid(), permission);
+ if (printLog) {
+ ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s",
+ func, Binder.getCallingPid(), Binder.getCallingUid(), permission);
+ }
return false;
}
@@ -2725,6 +2738,52 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
+ Bundle options) {
+ final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
+ "registerWindowContextListener", false /* printLog */);
+ final int callingUid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
+ if (dc == null) {
+ ProtoLog.w(WM_ERROR, "registerWindowContextListener: trying to add listener to"
+ + " a non-existing display:%d", displayId);
+ return false;
+ }
+ // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
+ // the feature b/155340867 is completed.
+ final DisplayArea da = dc.getAreaForWindowToken(type, options,
+ callerCanManageAppTokens, false /* roundedCornerOverlay */);
+ mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
+ callingUid);
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void unregisterWindowContextListener(IBinder clientToken) {
+ final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
+ "unregisterWindowContextListener", false /* printLog */);
+ final int callingUid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ if (mWindowContextListenerController.assertCallerCanRemoveListener(clientToken,
+ callerCanManageAppTokens, callingUid)) {
+ mWindowContextListenerController.unregisterWindowContainerListener(clientToken);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public boolean isWindowToken(IBinder binder) {
synchronized (mGlobalLock) {
final WindowToken windowToken = mRoot.getWindowToken(binder);
@@ -5106,9 +5165,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
case NOTIFY_ACTIVITY_DRAWN: {
- try {
- mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj);
- } catch (RemoteException e) {
+ final ActivityRecord activity = (ActivityRecord) msg.obj;
+ synchronized (mGlobalLock) {
+ if (activity.isAttached()) {
+ activity.getRootTask().notifyActivityDrawnLocked(activity);
+ }
}
break;
}
@@ -7717,6 +7778,9 @@ public class WindowManagerService extends IWindowManager.Stub
dc.mInputMethodControlTarget.hideInsets(
WindowInsets.Type.ime(), true /* fromIme */);
}
+ if (dc != null) {
+ dc.getInsetsStateController().getImeSourceProvider().setImeShowing(false);
+ }
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 5ced6a52050b..34875ed70889 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -419,6 +419,7 @@ class WindowToken extends WindowContainer<WindowState> {
reportWindowTokenRemovedToClient();
}
+ // TODO(b/159767464): Remove after we migrate to listener approach.
private void reportWindowTokenRemovedToClient() {
if (!shouldReportToClient()) {
return;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 6da9517743d2..d37c7b03dfb5 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -152,7 +152,7 @@ cc_defaults {
"android.hardware.power.stats@1.0",
"android.hardware.thermal@1.0",
"android.hardware.tv.input@1.0",
- "android.hardware.vibrator-cpp",
+ "android.hardware.vibrator-unstable-cpp",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9e0fb567975a..13450be73d88 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -238,16 +238,16 @@ public:
/* --- InputReaderPolicyInterface implementation --- */
- virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
- virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
- virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
- virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
- const InputDeviceIdentifier& identifier);
- virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
- virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
- jfloatArray matrixArr);
- virtual TouchAffineTransformation getTouchAffineTransformation(
- const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
+ void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
+ std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override;
+ void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+ std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+ const InputDeviceIdentifier& identifier) override;
+ std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override;
+ TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
+ int32_t surfaceRotation) override;
+
+ TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
/* --- InputDispatcherPolicyInterface implementation --- */
diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd
index 0d8c08c93ff2..501450398fc3 100644
--- a/services/core/xsd/device-state-config/device-state-config.xsd
+++ b/services/core/xsd/device-state-config/device-state-config.xsd
@@ -57,8 +57,8 @@
<xs:complexType name="sensorCondition">
<xs:sequence>
+ <xs:element name="type" type="xs:string" />
<xs:element name="name" type="xs:string" />
- <xs:element name="type" type="xs:positiveInteger" />
<xs:element name="value" type="numericRange" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt
index 667d1add5a98..b396af0fa0c6 100644
--- a/services/core/xsd/device-state-config/schema/current.txt
+++ b/services/core/xsd/device-state-config/schema/current.txt
@@ -44,10 +44,10 @@ package com.android.server.policy.devicestate.config {
public class SensorCondition {
ctor public SensorCondition();
method public String getName();
- method public java.math.BigInteger getType();
+ method public String getType();
method public java.util.List<com.android.server.policy.devicestate.config.NumericRange> getValue();
method public void setName(String);
- method public void setType(java.math.BigInteger);
+ method public void setType(String);
}
public class XmlParser {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 1a147b9f73e4..a281180d77d1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -40,14 +40,14 @@ import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.UserRestrictionsUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
@@ -300,7 +300,7 @@ class ActiveAdmin {
return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid));
}
- void writeToXml(XmlSerializer out)
+ void writeToXml(TypedXmlSerializer out)
throws IllegalArgumentException, IllegalStateException, IOException {
out.startTag(null, TAG_POLICIES);
info.writePoliciesToXml(out);
@@ -413,11 +413,11 @@ class ActiveAdmin {
}
if (isNetworkLoggingEnabled) {
out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(isNetworkLoggingEnabled));
- out.attribute(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS,
- Integer.toString(numNetworkLoggingNotifications));
- out.attribute(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION,
- Long.toString(lastNetworkLoggingNotificationTimeMs));
+ out.attributeBoolean(null, ATTR_VALUE, isNetworkLoggingEnabled);
+ out.attributeInt(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS,
+ numNetworkLoggingNotifications);
+ out.attributeLong(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION,
+ lastNetworkLoggingNotificationTimeMs);
out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
}
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
@@ -535,13 +535,13 @@ class ActiveAdmin {
}
}
- void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
+ void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
out.startTag(null, tag);
out.text(text);
out.endTag(null, tag);
}
- void writePackageListToXml(XmlSerializer out, String outerTag,
+ void writePackageListToXml(TypedXmlSerializer out, String outerTag,
List<String> packageList)
throws IllegalArgumentException, IllegalStateException, IOException {
if (packageList == null) {
@@ -550,35 +550,35 @@ class ActiveAdmin {
writeAttributeValuesToXml(out, outerTag, TAG_PACKAGE_LIST_ITEM, packageList);
}
- void writeAttributeValueToXml(XmlSerializer out, String tag, String value)
+ void writeAttributeValueToXml(TypedXmlSerializer out, String tag, String value)
throws IOException {
out.startTag(null, tag);
out.attribute(null, ATTR_VALUE, value);
out.endTag(null, tag);
}
- void writeAttributeValueToXml(XmlSerializer out, String tag, int value)
+ void writeAttributeValueToXml(TypedXmlSerializer out, String tag, int value)
throws IOException {
out.startTag(null, tag);
- out.attribute(null, ATTR_VALUE, Integer.toString(value));
+ out.attributeInt(null, ATTR_VALUE, value);
out.endTag(null, tag);
}
- void writeAttributeValueToXml(XmlSerializer out, String tag, long value)
+ void writeAttributeValueToXml(TypedXmlSerializer out, String tag, long value)
throws IOException {
out.startTag(null, tag);
- out.attribute(null, ATTR_VALUE, Long.toString(value));
+ out.attributeLong(null, ATTR_VALUE, value);
out.endTag(null, tag);
}
- void writeAttributeValueToXml(XmlSerializer out, String tag, boolean value)
+ void writeAttributeValueToXml(TypedXmlSerializer out, String tag, boolean value)
throws IOException {
out.startTag(null, tag);
- out.attribute(null, ATTR_VALUE, Boolean.toString(value));
+ out.attributeBoolean(null, ATTR_VALUE, value);
out.endTag(null, tag);
}
- void writeAttributeValuesToXml(XmlSerializer out, String outerTag, String innerTag,
+ void writeAttributeValuesToXml(TypedXmlSerializer out, String outerTag, String innerTag,
@NonNull Collection<String> values) throws IOException {
out.startTag(null, outerTag);
for (String value : values) {
@@ -589,7 +589,7 @@ class ActiveAdmin {
out.endTag(null, outerTag);
}
- void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies)
+ void readFromXml(TypedXmlPullParser parser, boolean shouldOverridePolicies)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -606,47 +606,33 @@ class ActiveAdmin {
info.readPoliciesFromXml(parser);
}
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
- mPasswordPolicy.quality = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.quality = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) {
- mPasswordPolicy.length = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.length = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) {
- passwordHistoryLength = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ passwordHistoryLength = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) {
- mPasswordPolicy.upperCase = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.upperCase = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) {
- mPasswordPolicy.lowerCase = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.lowerCase = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) {
- mPasswordPolicy.letters = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.letters = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) {
- mPasswordPolicy.numeric = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.numeric = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) {
- mPasswordPolicy.symbols = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.symbols = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
- mPasswordPolicy.nonLetter = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicy.nonLetter = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) {
- mPasswordPolicyAppliesToParent = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordPolicyAppliesToParent = parser.getAttributeBoolean(null, ATTR_VALUE);
} else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
- maximumTimeToUnlock = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ maximumTimeToUnlock = parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) {
- strongAuthUnlockTimeout = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ strongAuthUnlockTimeout = parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_MAX_FAILED_PASSWORD_WIPE.equals(tag)) {
- maximumFailedPasswordsForWipe = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ maximumFailedPasswordsForWipe = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_SPECIFIES_GLOBAL_PROXY.equals(tag)) {
- specifiesGlobalProxy = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ specifiesGlobalProxy = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_GLOBAL_PROXY_SPEC.equals(tag)) {
globalProxySpec =
parser.getAttributeValue(null, ATTR_VALUE);
@@ -654,48 +640,36 @@ class ActiveAdmin {
globalProxyExclusionList =
parser.getAttributeValue(null, ATTR_VALUE);
} else if (TAG_PASSWORD_EXPIRATION_TIMEOUT.equals(tag)) {
- passwordExpirationTimeout = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ passwordExpirationTimeout = parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_PASSWORD_EXPIRATION_DATE.equals(tag)) {
- passwordExpirationDate = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ passwordExpirationDate = parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_ENCRYPTION_REQUESTED.equals(tag)) {
- encryptionRequested = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ encryptionRequested = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_TEST_ONLY_ADMIN.equals(tag)) {
- testOnlyAdmin = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ testOnlyAdmin = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_DISABLE_CAMERA.equals(tag)) {
- disableCamera = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ disableCamera = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
- disableCallerId = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ disableCallerId = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) {
- disableContactsSearch = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ disableContactsSearch = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) {
- disableBluetoothContactSharing = Boolean.parseBoolean(parser
- .getAttributeValue(null, ATTR_VALUE));
+ disableBluetoothContactSharing =
+ parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) {
- disableScreenCapture = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ disableScreenCapture = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_REQUIRE_AUTO_TIME.equals(tag)) {
- requireAutoTime = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ requireAutoTime = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_FORCE_EPHEMERAL_USERS.equals(tag)) {
- forceEphemeralUsers = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ forceEphemeralUsers = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_IS_NETWORK_LOGGING_ENABLED.equals(tag)) {
- isNetworkLoggingEnabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
- lastNetworkLoggingNotificationTimeMs = Long.parseLong(
- parser.getAttributeValue(null, ATTR_LAST_NETWORK_LOGGING_NOTIFICATION));
- numNetworkLoggingNotifications = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS));
+ isNetworkLoggingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
+ lastNetworkLoggingNotificationTimeMs = parser.getAttributeLong(null,
+ ATTR_LAST_NETWORK_LOGGING_NOTIFICATION);
+ numNetworkLoggingNotifications = parser.getAttributeInt(null,
+ ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS);
} else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
- disabledKeyguardFeatures = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ disabledKeyguardFeatures = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
readAttributeValues(
parser, TAG_ACCOUNT_TYPE, accountTypesWithManagementDisabled);
@@ -721,7 +695,7 @@ class ActiveAdmin {
parser, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet);
} else if (TAG_SHORT_SUPPORT_MESSAGE.equals(tag)) {
type = parser.next();
- if (type == XmlPullParser.TEXT) {
+ if (type == TypedXmlPullParser.TEXT) {
shortSupportMessage = parser.getText();
} else {
Log.w(DevicePolicyManagerService.LOG_TAG,
@@ -729,7 +703,7 @@ class ActiveAdmin {
}
} else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
type = parser.next();
- if (type == XmlPullParser.TEXT) {
+ if (type == TypedXmlPullParser.TEXT) {
longSupportMessage = parser.getText();
} else {
Log.w(DevicePolicyManagerService.LOG_TAG,
@@ -740,22 +714,20 @@ class ActiveAdmin {
parentAdmin = new ActiveAdmin(info, /* parent */ true);
parentAdmin.readFromXml(parser, shouldOverridePolicies);
} else if (TAG_ORGANIZATION_COLOR.equals(tag)) {
- organizationColor = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ organizationColor = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_ORGANIZATION_NAME.equals(tag)) {
type = parser.next();
- if (type == XmlPullParser.TEXT) {
+ if (type == TypedXmlPullParser.TEXT) {
organizationName = parser.getText();
} else {
Log.w(DevicePolicyManagerService.LOG_TAG,
"Missing text when loading organization name");
}
} else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
- isLogoutEnabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_START_USER_SESSION_MESSAGE.equals(tag)) {
type = parser.next();
- if (type == XmlPullParser.TEXT) {
+ if (type == TypedXmlPullParser.TEXT) {
startUserSessionMessage = parser.getText();
} else {
Log.w(DevicePolicyManagerService.LOG_TAG,
@@ -763,7 +735,7 @@ class ActiveAdmin {
}
} else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
type = parser.next();
- if (type == XmlPullParser.TEXT) {
+ if (type == TypedXmlPullParser.TEXT) {
endUserSessionMessage = parser.getText();
} else {
Log.w(DevicePolicyManagerService.LOG_TAG,
@@ -779,24 +751,21 @@ class ActiveAdmin {
mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml(
parser);
} else if (TAG_SUSPEND_PERSONAL_APPS.equals(tag)) {
- mSuspendPersonalApps = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mSuspendPersonalApps = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_PROFILE_MAXIMUM_TIME_OFF.equals(tag)) {
mProfileMaximumTimeOffMillis =
- Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
+ parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) {
mProfileOffDeadline =
- Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
+ parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_ALWAYS_ON_VPN_PACKAGE.equals(tag)) {
mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE);
} else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) {
- mAlwaysOnVpnLockdown = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mAlwaysOnVpnLockdown = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_COMMON_CRITERIA_MODE.equals(tag)) {
- mCommonCriteriaMode = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) {
- mPasswordComplexity = Integer.parseInt(parser.getAttributeValue(null, ATTR_VALUE));
+ mPasswordComplexity = parser.getAttributeInt(null, ATTR_VALUE);
} else {
Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -804,14 +773,14 @@ class ActiveAdmin {
}
}
- private List<String> readPackageList(XmlPullParser parser,
+ private List<String> readPackageList(TypedXmlPullParser parser,
String tag) throws XmlPullParserException, IOException {
List<String> result = new ArrayList<String>();
int outerDepth = parser.getDepth();
int outerType;
- while ((outerType = parser.next()) != XmlPullParser.END_DOCUMENT
- && (outerType != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (outerType == XmlPullParser.END_TAG || outerType == XmlPullParser.TEXT) {
+ while ((outerType = parser.next()) != TypedXmlPullParser.END_DOCUMENT
+ && (outerType != TypedXmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (outerType == TypedXmlPullParser.END_TAG || outerType == TypedXmlPullParser.TEXT) {
continue;
}
String outerTag = parser.getName();
@@ -832,7 +801,7 @@ class ActiveAdmin {
}
private void readAttributeValues(
- XmlPullParser parser, String tag, Collection<String> result)
+ TypedXmlPullParser parser, String tag, Collection<String> result)
throws XmlPullParserException, IOException {
result.clear();
int outerDepthDAM = parser.getDepth();
@@ -854,7 +823,7 @@ class ActiveAdmin {
@NonNull
private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos(
- XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
+ TypedXmlPullParser parser, String tag) throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
int typeDAM;
final ArrayMap<String, TrustAgentInfo> result = new ArrayMap<>();
@@ -876,7 +845,7 @@ class ActiveAdmin {
return result;
}
- private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag)
+ private TrustAgentInfo getTrustAgentInfo(TypedXmlPullParser parser, String tag)
throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
int typeDAM;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 8a585ec62d00..31ba1991ee72 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -170,24 +170,19 @@ class DevicePolicyData {
}
if (policyData.mUserSetupComplete) {
if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true");
- out.attribute(null, ATTR_SETUP_COMPLETE,
- Boolean.toString(true));
+ out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true);
}
if (policyData.mPaired) {
- out.attribute(null, ATTR_DEVICE_PAIRED,
- Boolean.toString(true));
+ out.attributeBoolean(null, ATTR_DEVICE_PAIRED, true);
}
if (policyData.mDeviceProvisioningConfigApplied) {
- out.attribute(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED,
- Boolean.toString(true));
+ out.attributeBoolean(null, ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED, true);
}
if (policyData.mUserProvisioningState != DevicePolicyManager.STATE_USER_UNMANAGED) {
- out.attribute(null, ATTR_PROVISIONING_STATE,
- Integer.toString(policyData.mUserProvisioningState));
+ out.attributeInt(null, ATTR_PROVISIONING_STATE, policyData.mUserProvisioningState);
}
if (policyData.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
- out.attribute(null, ATTR_PERMISSION_POLICY,
- Integer.toString(policyData.mPermissionPolicy));
+ out.attributeInt(null, ATTR_PERMISSION_POLICY, policyData.mPermissionPolicy);
}
// Serialize delegations.
@@ -217,13 +212,13 @@ class DevicePolicyData {
if (policyData.mPasswordOwner >= 0) {
out.startTag(null, "password-owner");
- out.attribute(null, "value", Integer.toString(policyData.mPasswordOwner));
+ out.attributeInt(null, "value", policyData.mPasswordOwner);
out.endTag(null, "password-owner");
}
if (policyData.mFailedPasswordAttempts != 0) {
out.startTag(null, "failed-password-attempts");
- out.attribute(null, "value", Integer.toString(policyData.mFailedPasswordAttempts));
+ out.attributeInt(null, "value", policyData.mFailedPasswordAttempts);
out.endTag(null, "failed-password-attempts");
}
@@ -232,8 +227,7 @@ class DevicePolicyData {
// security reasons, we don't want to store the full set of active password metrics.
if (isFdeDevice) {
out.startTag(null, TAG_PASSWORD_VALIDITY);
- out.attribute(null, ATTR_VALUE,
- Boolean.toString(policyData.mPasswordValidAtLastCheckpoint));
+ out.attributeBoolean(null, ATTR_VALUE, policyData.mPasswordValidAtLastCheckpoint);
out.endTag(null, TAG_PASSWORD_VALIDITY);
}
@@ -252,19 +246,19 @@ class DevicePolicyData {
if (policyData.mLockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) {
out.startTag(null, TAG_LOCK_TASK_FEATURES);
- out.attribute(null, ATTR_VALUE, Integer.toString(policyData.mLockTaskFeatures));
+ out.attributeInt(null, ATTR_VALUE, policyData.mLockTaskFeatures);
out.endTag(null, TAG_LOCK_TASK_FEATURES);
}
if (policyData.mSecondaryLockscreenEnabled) {
out.startTag(null, TAG_SECONDARY_LOCK_SCREEN);
- out.attribute(null, ATTR_VALUE, Boolean.toString(true));
+ out.attributeBoolean(null, ATTR_VALUE, true);
out.endTag(null, TAG_SECONDARY_LOCK_SCREEN);
}
if (policyData.mStatusBarDisabled) {
out.startTag(null, TAG_STATUS_BAR);
- out.attribute(null, ATTR_DISABLED, Boolean.toString(policyData.mStatusBarDisabled));
+ out.attributeBoolean(null, ATTR_DISABLED, policyData.mStatusBarDisabled);
out.endTag(null, TAG_STATUS_BAR);
}
@@ -281,29 +275,25 @@ class DevicePolicyData {
if (policyData.mLastSecurityLogRetrievalTime >= 0) {
out.startTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL);
- out.attribute(null, ATTR_VALUE,
- Long.toString(policyData.mLastSecurityLogRetrievalTime));
+ out.attributeLong(null, ATTR_VALUE, policyData.mLastSecurityLogRetrievalTime);
out.endTag(null, TAG_LAST_SECURITY_LOG_RETRIEVAL);
}
if (policyData.mLastBugReportRequestTime >= 0) {
out.startTag(null, TAG_LAST_BUG_REPORT_REQUEST);
- out.attribute(null, ATTR_VALUE,
- Long.toString(policyData.mLastBugReportRequestTime));
+ out.attributeLong(null, ATTR_VALUE, policyData.mLastBugReportRequestTime);
out.endTag(null, TAG_LAST_BUG_REPORT_REQUEST);
}
if (policyData.mLastNetworkLogsRetrievalTime >= 0) {
out.startTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL);
- out.attribute(null, ATTR_VALUE,
- Long.toString(policyData.mLastNetworkLogsRetrievalTime));
+ out.attributeLong(null, ATTR_VALUE, policyData.mLastNetworkLogsRetrievalTime);
out.endTag(null, TAG_LAST_NETWORK_LOG_RETRIEVAL);
}
if (policyData.mAdminBroadcastPending) {
out.startTag(null, TAG_ADMIN_BROADCAST_PENDING);
- out.attribute(null, ATTR_VALUE,
- Boolean.toString(policyData.mAdminBroadcastPending));
+ out.attributeBoolean(null, ATTR_VALUE, policyData.mAdminBroadcastPending);
out.endTag(null, TAG_ADMIN_BROADCAST_PENDING);
}
@@ -315,8 +305,7 @@ class DevicePolicyData {
if (policyData.mPasswordTokenHandle != 0) {
out.startTag(null, TAG_PASSWORD_TOKEN_HANDLE);
- out.attribute(null, ATTR_VALUE,
- Long.toString(policyData.mPasswordTokenHandle));
+ out.attributeLong(null, ATTR_VALUE, policyData.mPasswordTokenHandle);
out.endTag(null, TAG_PASSWORD_TOKEN_HANDLE);
}
@@ -340,7 +329,7 @@ class DevicePolicyData {
if (policyData.mAppsSuspended) {
out.startTag(null, TAG_APPS_SUSPENDED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(policyData.mAppsSuspended));
+ out.attributeBoolean(null, ATTR_VALUE, policyData.mAppsSuspended);
out.endTag(null, TAG_APPS_SUSPENDED);
}
@@ -413,13 +402,13 @@ class DevicePolicyData {
if (Boolean.toString(true).equals(deviceProvisioningConfigApplied)) {
policy.mDeviceProvisioningConfigApplied = true;
}
- String provisioningState = parser.getAttributeValue(null, ATTR_PROVISIONING_STATE);
- if (!TextUtils.isEmpty(provisioningState)) {
- policy.mUserProvisioningState = Integer.parseInt(provisioningState);
+ int provisioningState = parser.getAttributeInt(null, ATTR_PROVISIONING_STATE, -1);
+ if (provisioningState != -1) {
+ policy.mUserProvisioningState = provisioningState;
}
- String permissionPolicy = parser.getAttributeValue(null, ATTR_PERMISSION_POLICY);
- if (!TextUtils.isEmpty(permissionPolicy)) {
- policy.mPermissionPolicy = Integer.parseInt(permissionPolicy);
+ int permissionPolicy = parser.getAttributeInt(null, ATTR_PERMISSION_POLICY, -1);
+ if (permissionPolicy != -1) {
+ policy.mPermissionPolicy = permissionPolicy;
}
parser.next();
@@ -472,37 +461,34 @@ class DevicePolicyData {
scopes.add(scope);
}
} else if ("failed-password-attempts".equals(tag)) {
- policy.mFailedPasswordAttempts = Integer.parseInt(
- parser.getAttributeValue(null, "value"));
+ policy.mFailedPasswordAttempts = parser.getAttributeInt(null, "value");
} else if ("password-owner".equals(tag)) {
- policy.mPasswordOwner = Integer.parseInt(
- parser.getAttributeValue(null, "value"));
+ policy.mPasswordOwner = parser.getAttributeInt(null, "value");
} else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
} else if (TAG_LOCK_TASK_FEATURES.equals(tag)) {
- policy.mLockTaskFeatures = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VALUE));
+ policy.mLockTaskFeatures = parser.getAttributeInt(null, ATTR_VALUE);
} else if (TAG_SECONDARY_LOCK_SCREEN.equals(tag)) {
- policy.mSecondaryLockscreenEnabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ policy.mSecondaryLockscreenEnabled =
+ parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else if (TAG_STATUS_BAR.equals(tag)) {
- policy.mStatusBarDisabled = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_DISABLED));
+ policy.mStatusBarDisabled =
+ parser.getAttributeBoolean(null, ATTR_DISABLED, false);
} else if (TAG_DO_NOT_ASK_CREDENTIALS_ON_BOOT.equals(tag)) {
policy.mDoNotAskCredentialsOnBoot = true;
} else if (TAG_AFFILIATION_ID.equals(tag)) {
policy.mAffiliationIds.add(parser.getAttributeValue(null, ATTR_ID));
} else if (TAG_LAST_SECURITY_LOG_RETRIEVAL.equals(tag)) {
- policy.mLastSecurityLogRetrievalTime = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ policy.mLastSecurityLogRetrievalTime =
+ parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_LAST_BUG_REPORT_REQUEST.equals(tag)) {
- policy.mLastBugReportRequestTime = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ policy.mLastBugReportRequestTime =
+ parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_LAST_NETWORK_LOG_RETRIEVAL.equals(tag)) {
- policy.mLastNetworkLogsRetrievalTime = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ policy.mLastNetworkLogsRetrievalTime =
+ parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_ADMIN_BROADCAST_PENDING.equals(tag)) {
String pending = parser.getAttributeValue(null, ATTR_VALUE);
policy.mAdminBroadcastPending = Boolean.toString(true).equals(pending);
@@ -515,12 +501,11 @@ class DevicePolicyData {
} else if (TAG_PASSWORD_VALIDITY.equals(tag)) {
if (isFdeDevice) {
// This flag is only used for FDE devices
- policy.mPasswordValidAtLastCheckpoint = Boolean.parseBoolean(
- parser.getAttributeValue(null, ATTR_VALUE));
+ policy.mPasswordValidAtLastCheckpoint =
+ parser.getAttributeBoolean(null, ATTR_VALUE, false);
}
} else if (TAG_PASSWORD_TOKEN_HANDLE.equals(tag)) {
- policy.mPasswordTokenHandle = Long.parseLong(
- parser.getAttributeValue(null, ATTR_VALUE));
+ policy.mPasswordTokenHandle = parser.getAttributeLong(null, ATTR_VALUE);
} else if (TAG_CURRENT_INPUT_METHOD_SET.equals(tag)) {
policy.mCurrentInputMethodSet = true;
} else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
@@ -530,7 +515,7 @@ class DevicePolicyData {
parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_APPS_SUSPENDED.equals(tag)) {
policy.mAppsSuspended =
- Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
+ parser.getAttributeBoolean(null, ATTR_VALUE, false);
} else {
Slog.w(TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bed450a196d9..edcf9e8399a8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4311,11 +4311,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updatePasswordQualityCacheForUserGroup(caller.getUserId());
saveSettingsLocked(caller.getUserId());
});
+
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_PASSWORD_COMPLEXITY)
+ .setAdmin(admin.info.getPackageName())
+ .setInt(passwordComplexity)
+ .setBoolean(calledOnParent)
+ .write();
}
logPasswordComplexityRequiredIfSecurityLogEnabled(admin.info.getComponent(),
caller.getUserId(), calledOnParent, passwordComplexity);
}
- //TODO: Log metrics.
}
private void logPasswordComplexityRequiredIfSecurityLogEnabled(ComponentName who, int userId,
@@ -5862,7 +5868,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Should include alias in authentication policy
try (KeyChainConnection connection = KeyChain.bindAsUser(mContext,
caller.getUserHandle())) {
- if (!containsAlias(connection.getService().getCredentialManagementAppPolicy(), alias)) {
+ // The policy will be null if there is no credential management app
+ AppUriAuthenticationPolicy policy =
+ connection.getService().getCredentialManagementAppPolicy();
+ if (policy == null || policy.getAppAndUriMappings().isEmpty()
+ || !containsAlias(policy, alias)) {
return false;
}
} catch (RemoteException | InterruptedException e) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 8ef69822750f..79a82b8e7175 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -43,22 +43,18 @@ import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
@@ -558,11 +554,10 @@ class Owners {
}
try {
InputStream input = new AtomicFile(file).openRead();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(input, StandardCharsets.UTF_8.name());
+ TypedXmlPullParser parser = Xml.resolvePullParser(input);
int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type!=XmlPullParser.START_TAG) {
+ while ((type=parser.next()) != TypedXmlPullParser.END_DOCUMENT) {
+ if (type!=TypedXmlPullParser.START_TAG) {
continue;
}
@@ -581,7 +576,7 @@ class Owners {
String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
String profileOwnerComponentStr =
parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
- int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USERID));
+ int userId = parser.getAttributeInt(null, ATTR_USERID);
OwnerInfo profileOwnerInfo = null;
if (profileOwnerComponentStr != null) {
ComponentName admin = ComponentName.unflattenFromString(
@@ -781,12 +776,12 @@ class Owners {
int type;
int depth = 0;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ while ((type = parser.next()) != TypedXmlPullParser.END_DOCUMENT) {
switch (type) {
- case XmlPullParser.START_TAG:
+ case TypedXmlPullParser.START_TAG:
depth++;
break;
- case XmlPullParser.END_TAG:
+ case TypedXmlPullParser.END_TAG:
depth--;
// fallthrough
default:
@@ -813,9 +808,9 @@ class Owners {
}
}
- abstract void writeInner(XmlSerializer out) throws IOException;
+ abstract void writeInner(TypedXmlSerializer out) throws IOException;
- abstract boolean readInner(XmlPullParser parser, int depth, String tag);
+ abstract boolean readInner(TypedXmlPullParser parser, int depth, String tag);
}
private class DeviceOwnerReadWriter extends FileReadWriter {
@@ -831,11 +826,11 @@ class Owners {
}
@Override
- void writeInner(XmlSerializer out) throws IOException {
+ void writeInner(TypedXmlSerializer out) throws IOException {
if (mDeviceOwner != null) {
mDeviceOwner.writeToXml(out, TAG_DEVICE_OWNER);
out.startTag(null, TAG_DEVICE_OWNER_CONTEXT);
- out.attribute(null, ATTR_USERID, String.valueOf(mDeviceOwnerUserId));
+ out.attributeInt(null, ATTR_USERID, mDeviceOwnerUserId);
out.endTag(null, TAG_DEVICE_OWNER_CONTEXT);
}
@@ -863,7 +858,7 @@ class Owners {
}
@Override
- boolean readInner(XmlPullParser parser, int depth, String tag) {
+ boolean readInner(TypedXmlPullParser parser, int depth, String tag) {
if (depth > 2) {
return true; // Ignore
}
@@ -873,13 +868,8 @@ class Owners {
mDeviceOwnerUserId = UserHandle.USER_SYSTEM; // Set default
break;
case TAG_DEVICE_OWNER_CONTEXT: {
- final String userIdString =
- parser.getAttributeValue(null, ATTR_USERID);
- try {
- mDeviceOwnerUserId = Integer.parseInt(userIdString);
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Error parsing user-id " + userIdString);
- }
+ mDeviceOwnerUserId = parser.getAttributeInt(null, ATTR_USERID,
+ mDeviceOwnerUserId);
break;
}
case TAG_DEVICE_INITIALIZER:
@@ -927,7 +917,7 @@ class Owners {
}
@Override
- void writeInner(XmlSerializer out) throws IOException {
+ void writeInner(TypedXmlSerializer out) throws IOException {
final OwnerInfo profileOwner = mProfileOwners.get(mUserId);
if (profileOwner != null) {
profileOwner.writeToXml(out, TAG_PROFILE_OWNER);
@@ -935,7 +925,7 @@ class Owners {
}
@Override
- boolean readInner(XmlPullParser parser, int depth, String tag) {
+ boolean readInner(TypedXmlPullParser parser, int depth, String tag) {
if (depth > 2) {
return true; // Ignore
}
@@ -985,7 +975,7 @@ class Owners {
this.isOrganizationOwnedDevice = isOrganizationOwnedDevice;
}
- public void writeToXml(XmlSerializer out, String tag) throws IOException {
+ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException {
out.startTag(null, tag);
out.attribute(null, ATTR_PACKAGE, packageName);
if (name != null) {
@@ -994,8 +984,7 @@ class Owners {
if (admin != null) {
out.attribute(null, ATTR_COMPONENT_NAME, admin.flattenToString());
}
- out.attribute(null, ATTR_USER_RESTRICTIONS_MIGRATED,
- String.valueOf(userRestrictionsMigrated));
+ out.attributeBoolean(null, ATTR_USER_RESTRICTIONS_MIGRATED, userRestrictionsMigrated);
if (remoteBugreportUri != null) {
out.attribute(null, ATTR_REMOTE_BUGREPORT_URI, remoteBugreportUri);
}
@@ -1003,13 +992,13 @@ class Owners {
out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash);
}
if (isOrganizationOwnedDevice) {
- out.attribute(null, ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE,
- String.valueOf(isOrganizationOwnedDevice));
+ out.attributeBoolean(null, ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE,
+ isOrganizationOwnedDevice);
}
out.endTag(null, tag);
}
- public static OwnerInfo readFromXml(XmlPullParser parser) {
+ public static OwnerInfo readFromXml(TypedXmlPullParser parser) {
final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
final String name = parser.getAttributeValue(null, ATTR_NAME);
final String componentName =
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
index 58ece0748d8b..289ed364e2a3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
@@ -29,18 +29,15 @@ import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
@@ -106,7 +103,7 @@ class TransferOwnershipMetadataManager {
return false;
}
- private void insertSimpleTag(XmlSerializer serializer, String tagName, String value)
+ private void insertSimpleTag(TypedXmlSerializer serializer, String tagName, String value)
throws IOException {
serializer.startTag(null, tagName);
serializer.text(value);
@@ -132,7 +129,7 @@ class TransferOwnershipMetadataManager {
return null;
}
- private Metadata parseMetadataFile(XmlPullParser parser)
+ private Metadata parseMetadataFile(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
int type;
final int outerDepth = parser.getDepth();
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 3e06194f8bee..b5e595a42ca9 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -554,7 +554,7 @@ public class DataManager {
@Nullable List<String> shortcutIds) {
@ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
| ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
- | ShortcutQuery.FLAG_MATCH_CACHED;
+ | ShortcutQuery.FLAG_MATCH_CACHED | ShortcutQuery.FLAG_GET_PERSONS_DATA;
return mShortcutServiceInternal.getShortcuts(
UserHandle.USER_SYSTEM, mContext.getPackageName(),
/*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 8fc5c085999e..03083c1f00b2 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -15,7 +15,7 @@
android_test {
name: "FrameworksMockingServicesTests",
- srcs: ["src/**/*.java"],
+ srcs: ["src/**/*.java", "src/**/*.kt"],
static_libs: [
"services.core",
@@ -29,6 +29,10 @@ android_test {
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"truth-prebuilt",
+ "hamcrest-library",
+ "servicestests-utils-mockito-extended",
+ "mockingservicestests-utils-mockito",
+ "servicestests-core-utils",
"testables",
// TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
"testng",
@@ -54,3 +58,17 @@ android_test {
enabled: false,
},
}
+
+java_library {
+ name: "mockingservicestests-utils-mockito",
+ srcs: [
+ "utils-mockito/**/*.kt",
+ ],
+ static_libs: [
+ "junit",
+ "mockito-target-extended-minus-junit4",
+ ],
+ libs: [
+ "android.test.runner",
+ ],
+} \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 7f86faa4b393..78bcc13c9265 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -145,7 +145,7 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = new ConnectivityController(mService);
when(mService.getMaxJobExecutionTimeMs(any()))
- .thenReturn(JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
+ .thenReturn(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS);
// Slow network is too slow
assertFalse(controller.isSatisfied(createJobStatus(job), net,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 8795d7711c63..4f3564d70416 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -35,6 +35,7 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -42,6 +43,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
@@ -76,6 +78,7 @@ import android.util.SparseBooleanArray;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerService.Constants;
@@ -269,8 +272,9 @@ public class QuotaControllerTest {
verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid));
assertFalse(foregroundUids.get(uid));
}
- } catch (RemoteException e) {
- fail("registerUidObserver threw exception: " + e.getMessage());
+ waitForQuietBackground();
+ } catch (Exception e) {
+ fail("exception encountered: " + e.getMessage());
}
}
@@ -323,6 +327,14 @@ public class QuotaControllerTest {
return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo);
}
+ private JobStatus createHpjJobStatus(String testTag, int jobId) {
+ JobInfo jobInfo = new JobInfo.Builder(jobId,
+ new ComponentName(mContext, "TestQuotaHpjJobService"))
+ .setForeground(true)
+ .build();
+ return createJobStatus(testTag, SOURCE_PACKAGE, CALLING_UID, jobInfo);
+ }
+
private JobStatus createJobStatus(String testTag, String packageName, int callingUid,
JobInfo jobInfo) {
JobStatus js = JobStatus.createFromJobInfo(
@@ -356,26 +368,49 @@ public class QuotaControllerTest {
}
}
+ private void waitForQuietBackground() throws Exception {
+ for (int i = 0; i < 5; ++i) {
+ if (!mQuotaController.isActiveBackgroundProcessing()) {
+ break;
+ }
+ Thread.sleep(500);
+ }
+ }
+
@Test
public void testSaveTimingSession() {
assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
- List<TimingSession> expected = new ArrayList<>();
+ List<TimingSession> expectedRegular = new ArrayList<>();
+ List<TimingSession> expectedHpj = new ArrayList<>();
TimingSession one = new TimingSession(1, 10, 1);
TimingSession two = new TimingSession(11, 20, 2);
TimingSession thr = new TimingSession(21, 30, 3);
-
- mQuotaController.saveTimingSession(0, "com.android.test", one);
- expected.add(one);
- assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
-
- mQuotaController.saveTimingSession(0, "com.android.test", two);
- expected.add(two);
- assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
-
- mQuotaController.saveTimingSession(0, "com.android.test", thr);
- expected.add(thr);
- assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+ TimingSession fou = new TimingSession(31, 40, 4);
+
+ mQuotaController.saveTimingSession(0, "com.android.test", one, false);
+ expectedRegular.add(one);
+ assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertTrue(
+ ArrayUtils.isEmpty(mQuotaController.getHpjTimingSessions(0, "com.android.test")));
+
+ mQuotaController.saveTimingSession(0, "com.android.test", two, false);
+ expectedRegular.add(two);
+ assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertTrue(
+ ArrayUtils.isEmpty(mQuotaController.getHpjTimingSessions(0, "com.android.test")));
+
+ mQuotaController.saveTimingSession(0, "com.android.test", thr, true);
+ expectedHpj.add(thr);
+ assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertEquals(expectedHpj, mQuotaController.getHpjTimingSessions(0, "com.android.test"));
+
+ mQuotaController.saveTimingSession(0, "com.android.test", fou, false);
+ mQuotaController.saveTimingSession(0, "com.android.test", fou, true);
+ expectedRegular.add(fou);
+ expectedHpj.add(fou);
+ assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertEquals(expectedHpj, mQuotaController.getHpjTimingSessions(0, "com.android.test"));
}
@Test
@@ -393,35 +428,48 @@ public class QuotaControllerTest {
// Way past the 24 hour boundary.
TimingSession fiv = createTimingSession(
now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4);
- List<TimingSession> expected = new ArrayList<>();
+ List<TimingSession> expectedRegular = new ArrayList<>();
+ List<TimingSession> expectedHpj = new ArrayList<>();
// Added in correct (chronological) order.
- expected.add(fou);
- expected.add(thr);
- expected.add(two);
- expected.add(one);
- mQuotaController.saveTimingSession(0, "com.android.test", fiv);
- mQuotaController.saveTimingSession(0, "com.android.test", fou);
- mQuotaController.saveTimingSession(0, "com.android.test", thr);
- mQuotaController.saveTimingSession(0, "com.android.test", two);
- mQuotaController.saveTimingSession(0, "com.android.test", one);
+ expectedRegular.add(fou);
+ expectedRegular.add(thr);
+ expectedRegular.add(two);
+ expectedRegular.add(one);
+ expectedHpj.add(fiv); // HPJ list should be unaffected
+ expectedHpj.add(fou);
+ expectedHpj.add(one);
+ mQuotaController.saveTimingSession(0, "com.android.test", fiv, false);
+ mQuotaController.saveTimingSession(0, "com.android.test", fou, false);
+ mQuotaController.saveTimingSession(0, "com.android.test", thr, false);
+ mQuotaController.saveTimingSession(0, "com.android.test", two, false);
+ mQuotaController.saveTimingSession(0, "com.android.test", one, false);
+ mQuotaController.saveTimingSession(0, "com.android.test", fiv, true);
+ mQuotaController.saveTimingSession(0, "com.android.test", fou, true);
+ mQuotaController.saveTimingSession(0, "com.android.test", one, true);
synchronized (mQuotaController.mLock) {
mQuotaController.deleteObsoleteSessionsLocked();
}
- assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertEquals(expectedRegular, mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertEquals(expectedHpj, mQuotaController.getHpjTimingSessions(0, "com.android.test"));
}
@Test
public void testOnAppRemovedLocked() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test.remove",
- createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test.remove",
createTimingSession(
- now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5),
+ false);
+ mQuotaController.saveTimingSession(0, "com.android.test.remove",
+ createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(0, "com.android.test.remove",
- createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true);
+ mQuotaController.saveTimingSession(0, "com.android.test.remove",
+ createTimingSession(now - (15 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true);
// Test that another app isn't affected.
TimingSession one = createTimingSession(
now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
@@ -431,8 +479,14 @@ public class QuotaControllerTest {
// Added in correct (chronological) order.
expected.add(two);
expected.add(one);
- mQuotaController.saveTimingSession(0, "com.android.test.stay", two);
- mQuotaController.saveTimingSession(0, "com.android.test.stay", one);
+ mQuotaController.saveTimingSession(0, "com.android.test.stay", two, false);
+ mQuotaController.saveTimingSession(0, "com.android.test.stay", one, false);
+ mQuotaController.saveTimingSession(0, "com.android.test.stay", one, true);
+
+ assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
+ assertNotNull(mQuotaController.getHpjTimingSessions(0, "com.android.test.remove"));
+ assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test.stay"));
+ assertNotNull(mQuotaController.getHpjTimingSessions(0, "com.android.test.stay"));
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
@@ -445,6 +499,7 @@ public class QuotaControllerTest {
mQuotaController.onAppRemovedLocked("com.android.test.remove", uid);
}
assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
+ assertNull(mQuotaController.getHpjTimingSessions(0, "com.android.test.remove"));
assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay"));
synchronized (mQuotaController.mLock) {
assertEquals(expectedStats,
@@ -462,23 +517,36 @@ public class QuotaControllerTest {
public void testOnUserRemovedLocked() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
- now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5),
+ false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (15 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), true);
// Test that another user isn't affected.
TimingSession one = createTimingSession(
now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
TimingSession two = createTimingSession(
now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
- List<TimingSession> expected = new ArrayList<>();
+ List<TimingSession> expectedRegular = new ArrayList<>();
+ List<TimingSession> expectedHpj = new ArrayList<>();
// Added in correct (chronological) order.
- expected.add(two);
- expected.add(one);
- mQuotaController.saveTimingSession(10, "com.android.test", two);
- mQuotaController.saveTimingSession(10, "com.android.test", one);
+ expectedRegular.add(two);
+ expectedRegular.add(one);
+ expectedHpj.add(one);
+ mQuotaController.saveTimingSession(10, "com.android.test", two, false);
+ mQuotaController.saveTimingSession(10, "com.android.test", one, false);
+ mQuotaController.saveTimingSession(10, "com.android.test", one, true);
+
+ assertNotNull(mQuotaController.getTimingSessions(0, "com.android.test"));
+ assertNotNull(mQuotaController.getHpjTimingSessions(0, "com.android.test"));
+ assertNotNull(mQuotaController.getTimingSessions(10, "com.android.test"));
+ assertNotNull(mQuotaController.getHpjTimingSessions(10, "com.android.test"));
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
@@ -489,7 +557,11 @@ public class QuotaControllerTest {
synchronized (mQuotaController.mLock) {
mQuotaController.onUserRemovedLocked(0);
assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
- assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test"));
+ assertNull(mQuotaController.getHpjTimingSessions(0, "com.android.test"));
+ assertEquals(expectedRegular,
+ mQuotaController.getTimingSessions(10, "com.android.test"));
+ assertEquals(expectedHpj,
+ mQuotaController.getHpjTimingSessions(10, "com.android.test"));
assertEquals(expectedStats,
mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
assertNotEquals(expectedStats,
@@ -502,17 +574,19 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Added in chronological order.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
- now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+ now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5),
+ false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
- now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+ now - (HOUR_IN_MILLIS - 10 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1),
+ false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3));
+ createTimingSession(now - 5 * MINUTE_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3), false);
// Test an app that hasn't had any activity.
ExecutionStats expectedStats = new ExecutionStats();
@@ -703,7 +777,6 @@ public class QuotaControllerTest {
@Test
public void testUpdateExecutionStatsLocked_WithTimer() {
- final long now = sElapsedRealtimeClock.millis();
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
ExecutionStats expectedStats = new ExecutionStats();
@@ -741,7 +814,7 @@ public class QuotaControllerTest {
// Add old session. Make sure values are combined correctly.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS),
- 10 * MINUTE_IN_MILLIS, 5));
+ 10 * MINUTE_IN_MILLIS, 5), false);
expectedStats.sessionCountInWindow = 1;
expectedStats.expirationTimeElapsed = sElapsedRealtimeClock.millis() + 18 * HOUR_IN_MILLIS;
@@ -794,13 +867,13 @@ public class QuotaControllerTest {
public void testGetExecutionStatsLocked_Values() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
ExecutionStats expectedStats = new ExecutionStats();
@@ -882,7 +955,7 @@ public class QuotaControllerTest {
advanceElapsedClock(3 * MINUTE_IN_MILLIS);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2));
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
ExecutionStats expectedStats = new ExecutionStats();
@@ -942,24 +1015,24 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
JobSchedulerService.sElapsedRealtimeClock.millis(),
- 5 * MINUTE_IN_MILLIS, 5));
+ 5 * MINUTE_IN_MILLIS, 5), false);
advanceElapsedClock(5 * MINUTE_IN_MILLIS);
advanceElapsedClock(5 * MINUTE_IN_MILLIS);
for (int j = 0; j < 5; ++j) {
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
JobSchedulerService.sElapsedRealtimeClock.millis(),
- MINUTE_IN_MILLIS, 2));
+ MINUTE_IN_MILLIS, 2), false);
advanceElapsedClock(MINUTE_IN_MILLIS);
advanceElapsedClock(54 * SECOND_IN_MILLIS);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
- JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1));
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
advanceElapsedClock(500);
advanceElapsedClock(400);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
- JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1));
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
advanceElapsedClock(100);
advanceElapsedClock(5 * SECOND_IN_MILLIS);
}
@@ -1095,13 +1168,13 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
final ExecutionStats originalStatsActive;
final ExecutionStats originalStatsWorking;
final ExecutionStats originalStatsFrequent;
@@ -1195,20 +1268,20 @@ public class QuotaControllerTest {
public void testGetMaxJobExecutionTimeLocked() {
mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
- 3 * MINUTE_IN_MILLIS, 5));
+ 3 * MINUTE_IN_MILLIS, 5), false);
JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0);
job.setStandbyBucket(RARE_INDEX);
setCharging();
synchronized (mQuotaController.mLock) {
- assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+ assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS,
mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
}
setDischarging();
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
synchronized (mQuotaController.mLock) {
- assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+ assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS,
mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
}
@@ -1220,7 +1293,7 @@ public class QuotaControllerTest {
}
setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
synchronized (mQuotaController.mLock) {
- assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+ assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS,
mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
mQuotaController.maybeStopTrackingJobLocked(job, null, false);
}
@@ -1242,17 +1315,17 @@ public class QuotaControllerTest {
// Close to RARE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
- 30 * SECOND_IN_MILLIS, 5));
+ 30 * SECOND_IN_MILLIS, 5), false);
// Far away from FREQUENT boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
// Overlap WORKING_SET boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
- 3 * MINUTE_IN_MILLIS, 5));
+ 3 * MINUTE_IN_MILLIS, 5), false);
// Close to ACTIVE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
setStandbyBucket(RARE_INDEX);
synchronized (mQuotaController.mLock) {
@@ -1306,7 +1379,8 @@ public class QuotaControllerTest {
// Overlap boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (24 * HOUR_IN_MILLIS + 8 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, 5));
+ now - (24 * HOUR_IN_MILLIS + 8 * MINUTE_IN_MILLIS), 4 * HOUR_IN_MILLIS, 5),
+ false);
setStandbyBucket(WORKING_INDEX);
synchronized (mQuotaController.mLock) {
@@ -1323,7 +1397,7 @@ public class QuotaControllerTest {
// Close to boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (24 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS),
- 4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5));
+ 4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5), false);
setStandbyBucket(WORKING_INDEX);
synchronized (mQuotaController.mLock) {
@@ -1339,7 +1413,8 @@ public class QuotaControllerTest {
// Far from boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5));
+ now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5),
+ false);
setStandbyBucket(WORKING_INDEX);
synchronized (mQuotaController.mLock) {
@@ -1366,10 +1441,11 @@ public class QuotaControllerTest {
createTimingSession(
now - (24 * HOUR_IN_MILLIS + 11 * MINUTE_IN_MILLIS),
4 * HOUR_IN_MILLIS,
- 5));
+ 5), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5),
+ false);
synchronized (mQuotaController.mLock) {
// Both max and bucket time have 8 minutes left.
@@ -1387,15 +1463,17 @@ public class QuotaControllerTest {
// Overlap boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5));
+ now - (24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5),
+ false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
now - (20 * HOUR_IN_MILLIS),
3 * HOUR_IN_MILLIS + 48 * MINUTE_IN_MILLIS,
- 5));
+ 5), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
- now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5),
+ false);
synchronized (mQuotaController.mLock) {
// Both max and bucket time have 8 minutes left.
@@ -1430,9 +1508,9 @@ public class QuotaControllerTest {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
@@ -1445,9 +1523,10 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
mQuotaController.saveTimingSession(0, "com.android.test.spam",
- createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
+ createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false);
mQuotaController.saveTimingSession(0, "com.android.test.spam",
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount),
+ false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount);
assertFalse(mQuotaController.isWithinQuotaLocked(
@@ -1455,9 +1534,10 @@ public class QuotaControllerTest {
}
mQuotaController.saveTimingSession(0, "com.android.test.frequent",
- createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000));
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000),
+ false);
mQuotaController.saveTimingSession(0, "com.android.test.frequent",
- createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500));
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false);
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinQuotaLocked(
0, "com.android.test.frequent", FREQUENT_INDEX));
@@ -1469,11 +1549,11 @@ public class QuotaControllerTest {
setDischarging();
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5));
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
@@ -1486,9 +1566,10 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW;
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
+ createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount),
+ false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount);
assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
@@ -1639,7 +1720,7 @@ public class QuotaControllerTest {
for (int i = 0; i < 7; ++i) {
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - ((10 - i) * MINUTE_IN_MILLIS), 30 * SECOND_IN_MILLIS,
- 2));
+ 2), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 2);
@@ -1673,7 +1754,7 @@ public class QuotaControllerTest {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final long end = now - (6 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
mQuotaController.saveTimingSession(0, "com.android.test",
- new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1));
+ new TimingSession(now - 6 * HOUR_IN_MILLIS, end, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleCleanupAlarmLocked();
}
@@ -1682,9 +1763,9 @@ public class QuotaControllerTest {
// Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleCleanupAlarmLocked();
}
@@ -1715,11 +1796,11 @@ public class QuotaControllerTest {
final long expectedAlarmTime =
(now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
+ createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
+ createTimingSession(now - 12 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1));
+ createTimingSession(now - 7 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
@@ -1727,7 +1808,7 @@ public class QuotaControllerTest {
verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 2 * HOUR_IN_MILLIS, 55 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(
SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
@@ -1772,7 +1853,7 @@ public class QuotaControllerTest {
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1784,7 +1865,7 @@ public class QuotaControllerTest {
final long expectedAlarmTime =
end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
- new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1));
+ new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1792,9 +1873,9 @@ public class QuotaControllerTest {
// Add some more sessions, but still in quota.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - (50 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1802,7 +1883,7 @@ public class QuotaControllerTest {
// Test when out of quota.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1836,7 +1917,7 @@ public class QuotaControllerTest {
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1846,7 +1927,7 @@ public class QuotaControllerTest {
final long start = now - (6 * HOUR_IN_MILLIS);
final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1854,9 +1935,9 @@ public class QuotaControllerTest {
// Add some more sessions, but still in quota.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1864,7 +1945,7 @@ public class QuotaControllerTest {
// Test when out of quota.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1901,7 +1982,7 @@ public class QuotaControllerTest {
// Test with timing sessions out of window.
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1915,7 +1996,7 @@ public class QuotaControllerTest {
start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS
+ mQcConstants.IN_QUOTA_BUFFER_MS;
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1923,9 +2004,9 @@ public class QuotaControllerTest {
// Add some more sessions, but still in quota.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - 3 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, 1), false);
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - HOUR_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1933,7 +2014,7 @@ public class QuotaControllerTest {
// Test when out of quota.
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1));
+ createTimingSession(now - HOUR_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
@@ -1960,17 +2041,17 @@ public class QuotaControllerTest {
// Affects rare bucket
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3));
+ createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3), false);
// Affects frequent and rare buckets
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3));
+ createTimingSession(now - 4 * HOUR_IN_MILLIS, 4 * MINUTE_IN_MILLIS, 3), false);
// Affects working, frequent, and rare buckets
final long outOfQuotaTime = now - HOUR_IN_MILLIS;
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10));
+ createTimingSession(outOfQuotaTime, 7 * MINUTE_IN_MILLIS, 10), false);
// Affects all buckets
mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3));
+ createTimingSession(now - 5 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS, 3), false);
InOrder inOrder = inOrder(mAlarmManager);
@@ -2143,9 +2224,9 @@ public class QuotaControllerTest {
// the quota.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS),
- 3 * MINUTE_IN_MILLIS + contributionMs, 3));
+ 3 * MINUTE_IN_MILLIS + contributionMs, 3), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2));
+ createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2), false);
// Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
// is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS
@@ -2175,9 +2256,9 @@ public class QuotaControllerTest {
// the quota.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (24 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS),
- 3 * MINUTE_IN_MILLIS + contributionMs, 3));
+ 3 * MINUTE_IN_MILLIS + contributionMs, 3), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - 20 * HOUR_IN_MILLIS, remainingTimeMs, 300));
+ createTimingSession(now - 20 * HOUR_IN_MILLIS, remainingTimeMs, 300), false);
// Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
// is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS
@@ -2217,6 +2298,16 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
10 * SECOND_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 7 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 2 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 90 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 27 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_WINDOW_SIZE_MS, 12 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_TOP_APP_MS, 87 * SECOND_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_INTERACTION_MS, 86 * SECOND_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS, 85 * SECOND_IN_MILLIS);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -2244,6 +2335,16 @@ public class QuotaControllerTest {
assertEquals(10 * SECOND_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
+ assertEquals(2 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[ACTIVE_INDEX]);
+ assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[WORKING_INDEX]);
+ assertEquals(HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[FREQUENT_INDEX]);
+ assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RARE_INDEX]);
+ assertEquals(27 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RESTRICTED_INDEX]);
+ assertEquals(12 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitWindowSizeMs());
+ assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getHpjTopAppTimeChunkSizeMs());
+ assertEquals(87 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardTopAppMs());
+ assertEquals(86 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardInteractionMs());
+ assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardNotificationSeenMs());
}
@Test
@@ -2272,6 +2373,16 @@ public class QuotaControllerTest {
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 0);
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, -1);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_WINDOW_SIZE_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_TOP_APP_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_INTERACTION_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS, -1);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(0, mQuotaController.getInQuotaBufferMs());
@@ -2296,6 +2407,16 @@ public class QuotaControllerTest {
assertEquals(0, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(0, mQuotaController.getMinQuotaCheckDelayMs());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[ACTIVE_INDEX]);
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[WORKING_INDEX]);
+ assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[FREQUENT_INDEX]);
+ assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RARE_INDEX]);
+ assertEquals(0, mQuotaController.getHpjLimitsMs()[RESTRICTED_INDEX]);
+ assertEquals(HOUR_IN_MILLIS, mQuotaController.getHpjLimitWindowSizeMs());
+ assertEquals(1, mQuotaController.getHpjTopAppTimeChunkSizeMs());
+ assertEquals(10 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardTopAppMs());
+ assertEquals(5 * SECOND_IN_MILLIS, mQuotaController.getHpjRewardInteractionMs());
+ assertEquals(0, mQuotaController.getHpjRewardNotificationSeenMs());
// Invalid configurations.
// In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
@@ -2318,6 +2439,16 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_TOP_APP_TIME_CHUNK_SIZE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_INTERACTION_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
@@ -2332,6 +2463,16 @@ public class QuotaControllerTest {
assertEquals(15 * MINUTE_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[ACTIVE_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[WORKING_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[FREQUENT_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RARE_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitsMs()[RESTRICTED_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getHpjLimitWindowSizeMs());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjTopAppTimeChunkSizeMs());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjRewardTopAppMs());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getHpjRewardInteractionMs());
+ assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getHpjRewardNotificationSeenMs());
}
/** Tests that TimingSessions aren't saved when the device is charging. */
@@ -2937,7 +3078,7 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
- 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+ 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false);
InOrder inOrder = inOrder(mJobSchedulerService);
@@ -3031,7 +3172,7 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(
JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
- 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1));
+ 10 * MINUTE_IN_MILLIS - remainingTimeMs, 1), false);
// Start the job.
synchronized (mQuotaController.mLock) {
@@ -3069,10 +3210,10 @@ public class QuotaControllerTest {
// window, so as the package "reaches its quota" it will have more to keep running.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 2 * HOUR_IN_MILLIS,
- 10 * SECOND_IN_MILLIS - remainingTimeMs, 1));
+ 10 * SECOND_IN_MILLIS - remainingTimeMs, 1), false);
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - HOUR_IN_MILLIS,
- 9 * MINUTE_IN_MILLIS + 50 * SECOND_IN_MILLIS, 1));
+ 9 * MINUTE_IN_MILLIS + 50 * SECOND_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
assertEquals(remainingTimeMs,
@@ -3098,7 +3239,9 @@ public class QuotaControllerTest {
}
// Handler is told to check when the quota will be consumed, not when the initial
// remaining time is over.
- verify(handler, atLeast(1)).sendMessageDelayed(any(), eq(10 * SECOND_IN_MILLIS));
+ verify(handler, atLeast(1)).sendMessageDelayed(
+ argThat(msg -> msg.what == QuotaController.MSG_REACHED_QUOTA),
+ eq(10 * SECOND_IN_MILLIS));
verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs));
// After 10 seconds, the job should finally be out of quota.
@@ -3242,4 +3385,1337 @@ public class QuotaControllerTest {
verify(mAlarmManager, times(1))
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
}
+
+ @Test
+ public void testGetRemainingHpjExecutionTimeLocked_NoHistory() {
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ setStandbyBucket(i);
+ assertEquals("Got wrong remaining HPJ execution time for bucket #" + i,
+ limits[i],
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ public void testGetRemainingHpjExecutionTimeLocked_AllSessionsWithinWindow() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - mQcConstants.HPJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5),
+ true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ setStandbyBucket(i);
+ assertEquals("Got wrong remaining HPJ execution time for bucket #" + i,
+ i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS),
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ public void testGetRemainingHpjExecutionTimeLocked_OneSessionStraddlesEdge() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.onUserRemovedLocked(SOURCE_USER_ID);
+ }
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+
+ setStandbyBucket(i);
+ assertEquals("Got wrong remaining HPJ execution time for bucket #" + i,
+ i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS),
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ public void testGetRemainingHpjExecutionTimeLocked_WithStaleSessions() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.onUserRemovedLocked(SOURCE_USER_ID);
+ }
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 10 * MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 5 * MINUTE_IN_MILLIS),
+ MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+
+ setStandbyBucket(i);
+ assertEquals("Got wrong remaining HPJ execution time for bucket #" + i,
+ i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS),
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ /**
+ * Tests that getRemainingHpjExecutionTimeLocked returns the correct stats soon after device
+ * startup.
+ */
+ @Test
+ public void testGetRemainingHpjExecutionTimeLocked_BeginningOfTime() {
+ // Set time to 3 minutes after boot.
+ advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+ advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(150 * SECOND_IN_MILLIS, 15 * SECOND_IN_MILLIS, 5), true);
+
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ setStandbyBucket(i);
+ assertEquals("Got wrong remaining HPJ execution time for bucket #" + i,
+ i == NEVER_INDEX ? 0 : (limits[i] - 75 * SECOND_IN_MILLIS),
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ public void testGetTimeUntilHpjQuotaConsumedLocked_NoHistory() {
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ setStandbyBucket(i);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + i,
+ limits[i], mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ public void testGetTimeUntilHpjQuotaConsumedLocked_AllSessionsWithinWindow() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 20 * MINUTE_IN_MILLIS, 2 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ setStandbyBucket(i);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + i,
+ i == NEVER_INDEX ? 0 : (limits[i] - 5 * MINUTE_IN_MILLIS),
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ @Test
+ public void testGetTimeUntilHpjQuotaConsumedLocked_SessionsAtEdgeOfWindow() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - mQcConstants.HPJ_WINDOW_SIZE_MS, MINUTE_IN_MILLIS, 5),
+ true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS - 2 * MINUTE_IN_MILLIS),
+ MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS - 10 * MINUTE_IN_MILLIS),
+ MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS);
+
+ setStandbyBucket(ACTIVE_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + ACTIVE_INDEX,
+ 28 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(WORKING_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + WORKING_INDEX,
+ 18 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(FREQUENT_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + FREQUENT_INDEX,
+ 13 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RARE_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RARE_INDEX,
+ 7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RESTRICTED_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RESTRICTED_INDEX,
+ MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ @Test
+ public void testGetTimeUntilHpjQuotaConsumedLocked_OneSessionStraddlesEdge() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5), true);
+
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS);
+
+ setStandbyBucket(ACTIVE_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + ACTIVE_INDEX,
+ 26 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(WORKING_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + WORKING_INDEX,
+ 16 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(FREQUENT_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + FREQUENT_INDEX,
+ 11 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RARE_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RARE_INDEX,
+ 6 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ setStandbyBucket(RESTRICTED_INDEX);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + RESTRICTED_INDEX,
+ MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ @Test
+ public void testGetTimeUntilHpjQuotaConsumedLocked_WithStaleSessions() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+ List<TimingSession> timingSessions = new ArrayList<>();
+ timingSessions.add(
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 10 * MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5));
+ timingSessions.add(
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + 5 * MINUTE_IN_MILLIS),
+ MINUTE_IN_MILLIS, 5));
+ timingSessions.add(
+ createTimingSession(now - (mQcConstants.HPJ_WINDOW_SIZE_MS + MINUTE_IN_MILLIS),
+ 2 * MINUTE_IN_MILLIS, 5));
+ timingSessions.add(
+ createTimingSession(now - 40 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5));
+ timingSessions.add(
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5));
+ timingSessions.add(
+ createTimingSession(now - 20 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5));
+ timingSessions.add(
+ createTimingSession(now - 10 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 5));
+
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RESTRICTED_MS, 5 * MINUTE_IN_MILLIS);
+
+ runTestGetTimeUntilHpjQuotaConsumedLocked(
+ timingSessions, ACTIVE_INDEX, 26 * MINUTE_IN_MILLIS);
+ runTestGetTimeUntilHpjQuotaConsumedLocked(
+ timingSessions, WORKING_INDEX, 16 * MINUTE_IN_MILLIS);
+ runTestGetTimeUntilHpjQuotaConsumedLocked(
+ timingSessions, FREQUENT_INDEX, 11 * MINUTE_IN_MILLIS);
+ runTestGetTimeUntilHpjQuotaConsumedLocked(timingSessions, RARE_INDEX, 6 * MINUTE_IN_MILLIS);
+ runTestGetTimeUntilHpjQuotaConsumedLocked(
+ timingSessions, RESTRICTED_INDEX, MINUTE_IN_MILLIS);
+ }
+
+ /**
+ * Tests that getTimeUntilHpjQuotaConsumedLocked returns the correct stats soon after device
+ * startup.
+ */
+ @Test
+ public void testGetTimeUntilHpjQuotaConsumedLocked_BeginningOfTime() {
+ // Set time to 3 minutes after boot.
+ advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+ advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(150 * SECOND_IN_MILLIS, 15 * SECOND_IN_MILLIS, 5), true);
+
+ final long[] limits = mQuotaController.getHpjLimitsMs();
+ for (int i = 0; i < limits.length; ++i) {
+ setStandbyBucket(i);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + i,
+ limits[i], // All existing sessions will phase out
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ private void runTestGetTimeUntilHpjQuotaConsumedLocked(
+ List<TimingSession> timingSessions, int bucketIndex, long expectedValue) {
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.onUserRemovedLocked(SOURCE_USER_ID);
+ }
+ if (timingSessions != null) {
+ for (TimingSession session : timingSessions) {
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, session, true);
+ }
+ }
+
+ setStandbyBucket(bucketIndex);
+ assertEquals("Got wrong time until HPJ quota consumed for bucket #" + bucketIndex,
+ expectedValue,
+ mQuotaController.getTimeUntilHpjQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_Hpj() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ final int standbyBucket = WORKING_INDEX;
+ setStandbyBucket(standbyBucket);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS);
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ synchronized (mQuotaController.mLock) {
+ // No sessions saved yet.
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions out of window.
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 25 * HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS, 1), true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test with timing sessions in window but still in quota.
+ final long end = now - (22 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
+ final long expectedAlarmTime = now + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS;
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ new TimingSession(now - 22 * HOUR_IN_MILLIS, end, 1), true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Add some more sessions, but still in quota.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (50 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 1), true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Test when out of quota.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 30 * MINUTE_IN_MILLIS, 6 * MINUTE_IN_MILLIS, 1), true);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Alarm already scheduled, so make sure it's not scheduled again.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ }
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+
+ /** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_Hpj_BucketChange() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_ACTIVE_MS, 30 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_WORKING_MS, 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_FREQUENT_MS, 15 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_HPJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS);
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Affects active bucket
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 12 * HOUR_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3), true);
+ // Affects active and working buckets
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 4 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 3), true);
+ // Affects active, working, and frequent buckets
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 10), true);
+ // Affects all buckets
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 5 * MINUTE_IN_MILLIS, 10 * MINUTE_IN_MILLIS, 3), true);
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ // Start in ACTIVE bucket.
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
+ }
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+ inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
+
+ // And down from there.
+ setStandbyBucket(WORKING_INDEX);
+ final long expectedWorkingAlarmTime =
+ (now - 4 * HOUR_IN_MILLIS) + (24 * HOUR_IN_MILLIS)
+ + mQcConstants.IN_QUOTA_BUFFER_MS;
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
+ }
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ setStandbyBucket(FREQUENT_INDEX);
+ final long expectedFrequentAlarmTime =
+ (now - HOUR_IN_MILLIS) + (24 * HOUR_IN_MILLIS) + mQcConstants.IN_QUOTA_BUFFER_MS;
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
+ }
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ setStandbyBucket(RARE_INDEX);
+ final long expectedRareAlarmTime =
+ (now - 5 * MINUTE_IN_MILLIS) + (24 * HOUR_IN_MILLIS)
+ + mQcConstants.IN_QUOTA_BUFFER_MS;
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
+ }
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // And back up again.
+ setStandbyBucket(FREQUENT_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
+ }
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ setStandbyBucket(WORKING_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
+ }
+ inOrder.verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, ACTIVE_INDEX);
+ }
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+ inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class));
+ }
+
+ /**
+ * Tests that the start alarm is properly rescheduled if the earliest session that contributes
+ * to the app being out of quota contributes less than the quota buffer time.
+ */
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_Hpj_SmallRollingQuota() {
+ // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+ // because it schedules an alarm too. Prevent it from doing so.
+ spyOn(mQuotaController);
+ doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
+
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setStandbyBucket(WORKING_INDEX);
+ final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2;
+ final long remainingTimeMs = mQcConstants.HPJ_LIMIT_WORKING_MS - contributionMs;
+
+ // Session straddles edge of bucket window. Only the contribution should be counted towards
+ // the quota.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (24 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS + contributionMs, 3), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - 23 * HOUR_IN_MILLIS, remainingTimeMs, 2), true);
+ // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which
+ // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session.
+ final long expectedAlarmTime =
+ now + HOUR_IN_MILLIS + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
+ }
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+ }
+
+ /** Tests that TimingSessions aren't saved when the device is charging. */
+ @Test
+ public void testHpjTimerTracking_Charging() {
+ setCharging();
+
+ JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_Charging", 1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /** Tests that TimingSessions are saved properly when the device is discharging. */
+ @Test
+ public void testHpjTimerTracking_Discharging() {
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_BACKUP);
+
+ JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_Discharging", 1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ List<TimingSession> expected = new ArrayList<>();
+
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Test overlapping jobs.
+ JobStatus jobStatus2 = createHpjJobStatus("testHpjTimerTracking_Discharging", 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ }
+
+ JobStatus jobStatus3 = createHpjJobStatus("testHpjTimerTracking_Discharging", 3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ }
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ }
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
+ expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that TimingSessions are saved properly when the device alternates between
+ * charging and discharging.
+ */
+ @Test
+ public void testHpjTimerTracking_ChargingAndDischarging() {
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+ JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_ChargingAndDischarging", 1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+ JobStatus jobStatus2 = createHpjJobStatus("testHpjTimerTracking_ChargingAndDischarging", 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ }
+ JobStatus jobStatus3 = createHpjJobStatus("testHpjTimerTracking_ChargingAndDischarging", 3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ }
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expected = new ArrayList<>();
+
+ // A job starting while charging. Only the portion that runs during the discharging period
+ // should be counted.
+ setCharging();
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ setDischarging();
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, jobStatus, true);
+ }
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // One job starts while discharging, spans a charging session, and ends after the charging
+ // session. Only the portions during the discharging periods should be counted. This should
+ // result in two TimingSessions. A second job starts while discharging and ends within the
+ // charging session. Only the portion during the first discharging portion should be
+ // counted. A third job starts and ends within the charging session. The third job
+ // shouldn't be included in either job count.
+ setDischarging();
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ setCharging();
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ setDischarging();
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // A job starting while discharging and ending while charging. Only the portion that runs
+ // during the discharging period should be counted.
+ setDischarging();
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ setCharging();
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /** Tests that TimingSessions are saved properly when all the jobs are background jobs. */
+ @Test
+ public void testHpjTimerTracking_AllBackground() {
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+
+ JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_AllBackground", 1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ List<TimingSession> expected = new ArrayList<>();
+
+ // Test single job.
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ expected.add(createTimingSession(start, 5 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Test overlapping jobs.
+ JobStatus jobStatus2 = createHpjJobStatus("testHpjTimerTracking_AllBackground", 2);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus2, null);
+ }
+
+ JobStatus jobStatus3 = createHpjJobStatus("testHpjTimerTracking_AllBackground", 3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus3, null);
+ }
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus3);
+ }
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus3, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus2, null, false);
+ }
+ expected.add(createTimingSession(start, MINUTE_IN_MILLIS, 3));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /** Tests that Timers don't count foreground jobs. */
+ @Test
+ public void testHpjTimerTracking_AllForeground() {
+ setDischarging();
+
+ JobStatus jobStatus = createHpjJobStatus("testHpjTimerTracking_AllForeground", 1);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ // Change to a state that should still be considered foreground.
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that Timers properly track sessions when switching between foreground and background
+ * states.
+ */
+ @Test
+ public void testHpjTimerTracking_ForegroundAndBackground() {
+ setDischarging();
+
+ JobStatus jobBg1 = createHpjJobStatus("testHpjTimerTracking_ForegroundAndBackground", 1);
+ JobStatus jobBg2 = createHpjJobStatus("testHpjTimerTracking_ForegroundAndBackground", 2);
+ JobStatus jobFg3 = createHpjJobStatus("testHpjTimerTracking_ForegroundAndBackground", 3);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ }
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expected = new ArrayList<>();
+
+ // UID starts out inactive.
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Bg job starts while inactive, spans an entire active session, and ends after the
+ // active session.
+ // App switching to foreground state then fg job starts.
+ // App remains in foreground state after coming to foreground, so there should only be one
+ // session.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg3);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ }
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Bg job 1 starts, then fg job starts. Bg job 1 job ends. Shortly after, uid goes
+ // "inactive" and then bg job 2 starts. Then fg job ends.
+ // This should result in two TimingSessions:
+ // * The first should have a count of 1
+ // * The second should have a count of 2 since it will include both jobs
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg3, null);
+ }
+ setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg3);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobFg3, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ }
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that Timers properly track overlapping top and background jobs.
+ */
+ @Test
+ public void testHpjTimerTracking_TopAndNonTop() {
+ setDischarging();
+
+ JobStatus jobBg1 = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 1);
+ JobStatus jobBg2 = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 2);
+ JobStatus jobFg1 = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 3);
+ JobStatus jobTop = createHpjJobStatus("testHpjTimerTracking_TopAndNonTop", 4);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+ }
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expected = new ArrayList<>();
+
+ // UID starts out inactive.
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Bg job starts while inactive, spans an entire active session, and ends after the
+ // active session.
+ // App switching to top state then fg job starts.
+ // App remains in top state after coming to top, so there should only be one
+ // session.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ }
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Bg job 1 starts, then top job starts. Bg job 1 job ends. Then app goes to
+ // foreground_service and a new job starts. Shortly after, uid goes
+ // "inactive" and then bg job 2 starts. Then top job ends, followed by bg and fg jobs.
+ // This should result in two TimingSessions:
+ // * The first should have a count of 1
+ // * The second should have a count of 2, which accounts for the bg2 and fg, but not top
+ // jobs.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobTop, null);
+ }
+ setProcessState(ActivityManager.PROCESS_STATE_LAST_ACTIVITY);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ expected.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, jobBg1, true);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobFg1);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS); // UID "inactive" now
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobTop, null, false);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+ mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+ }
+ expected.add(createTimingSession(start, 20 * SECOND_IN_MILLIS, 2));
+ assertEquals(expected,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that HPJs aren't stopped when an app runs out of quota.
+ */
+ @Test
+ public void testHpjTracking_OutOfQuota_ForegroundAndBackground() {
+ setDischarging();
+
+ JobStatus jobBg =
+ createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 1);
+ JobStatus jobTop =
+ createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 2);
+ JobStatus jobUnstarted =
+ createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 3);
+ trackJobs(jobBg, jobTop, jobUnstarted);
+ setStandbyBucket(WORKING_INDEX, jobTop, jobBg, jobUnstarted);
+ // Now the package only has 20 seconds to run.
+ final long remainingTimeMs = 20 * SECOND_IN_MILLIS;
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis() - HOUR_IN_MILLIS,
+ mQcConstants.HPJ_LIMIT_WORKING_MS - remainingTimeMs, 1), true);
+
+ InOrder inOrder = inOrder(mJobSchedulerService);
+
+ // UID starts out inactive.
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ // Start the job.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobBg);
+ }
+ advanceElapsedClock(remainingTimeMs / 2);
+ // New job starts after UID is in the foreground. Since the app is now in the foreground, it
+ // should continue to have remainingTimeMs / 2 time remaining.
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop);
+ }
+ advanceElapsedClock(remainingTimeMs);
+
+ // Wait for some extra time to allow for job processing.
+ inOrder.verify(mJobSchedulerService,
+ timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+ .onControllerStateChanged();
+ synchronized (mQuotaController.mLock) {
+ assertEquals(remainingTimeMs / 2,
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ // Go to a background state.
+ setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+ advanceElapsedClock(remainingTimeMs / 2 + 1);
+ inOrder.verify(mJobSchedulerService,
+ timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged();
+ // Top and bg HPJs should still be allowed to run since they started before the app ran
+ // out of quota.
+ assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertFalse(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ synchronized (mQuotaController.mLock) {
+ assertTrue(
+ 0 >= mQuotaController
+ .getRemainingHpjExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ // New jobs to run.
+ JobStatus jobBg2 =
+ createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 4);
+ JobStatus jobTop2 =
+ createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 5);
+ JobStatus jobFg =
+ createHpjJobStatus("testHpjTracking_OutOfQuota_ForegroundAndBackground", 6);
+ setStandbyBucket(WORKING_INDEX, jobBg2, jobTop2, jobFg);
+
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ // Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota.
+ inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged();
+ trackJobs(jobTop2, jobFg);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobTop2);
+ }
+ assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+
+ // App still in foreground so everything should be in quota.
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+
+ advanceElapsedClock(20 * SECOND_IN_MILLIS);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
+ .onControllerStateChanged();
+ // App is now in background and out of quota. Fg should now change to out of quota since it
+ // wasn't started. Top should remain in quota since it started when the app was in TOP.
+ assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ trackJobs(jobBg2);
+ assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ assertFalse(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ synchronized (mQuotaController.mLock) {
+ assertTrue(
+ 0 >= mQuotaController
+ .getRemainingHpjExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
+ /**
+ * Tests that Timers properly track overlapping top and background jobs.
+ */
+ @Test
+ public void testHpjTimerTrackingSeparateFromRegularTracking() {
+ setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
+ JobStatus jobReg1 = createJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 1);
+ JobStatus jobHpj1 =
+ createHpjJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 2);
+ JobStatus jobReg2 = createJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 3);
+ JobStatus jobHpj2 =
+ createHpjJobStatus("testHpjTimerTrackingSeparateFromRegularTracking", 4);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobReg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobHpj1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobReg2, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobHpj2, null);
+ }
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ List<TimingSession> expectedRegular = new ArrayList<>();
+ List<TimingSession> expectedHpj = new ArrayList<>();
+
+ // First, regular job runs by itself.
+ long start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobReg1);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobReg1, jobReg1, true);
+ }
+ expectedRegular.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expectedRegular,
+ mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertNull(mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Next, HPJ runs by itself.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobHpj1);
+ }
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobHpj1, null, false);
+ }
+ expectedHpj.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expectedRegular,
+ mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(expectedHpj,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ advanceElapsedClock(SECOND_IN_MILLIS);
+
+ // Finally, a regular job and HPJ happen to overlap runs.
+ start = JobSchedulerService.sElapsedRealtimeClock.millis();
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobHpj2);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(jobReg2);
+ }
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobHpj2, null, false);
+ }
+ expectedHpj.add(createTimingSession(start, 10 * SECOND_IN_MILLIS, 1));
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStopTrackingJobLocked(jobReg2, null, false);
+ }
+ expectedRegular.add(
+ createTimingSession(start + 5 * SECOND_IN_MILLIS, 10 * SECOND_IN_MILLIS, 1));
+ assertEquals(expectedRegular,
+ mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(expectedHpj,
+ mQuotaController.getHpjTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ /**
+ * Tests that a job is properly handled when it's at the edge of its quota and the old quota is
+ * being phased out.
+ */
+ @Test
+ public void testHpjTracking_RollingQuota() {
+ JobStatus jobStatus = createHpjJobStatus("testHpjTracking_RollingQuota", 1);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
+ }
+ setStandbyBucket(WORKING_INDEX, jobStatus);
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
+ Handler handler = mQuotaController.getHandler();
+ spyOn(handler);
+
+ long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long remainingTimeMs = SECOND_IN_MILLIS;
+ // The package only has one second to run, but this session is at the edge of the rolling
+ // window, so as the package "reaches its quota" it will have more to keep running.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - mQcConstants.HPJ_WINDOW_SIZE_MS,
+ 10 * SECOND_IN_MILLIS - remainingTimeMs, 1), true);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - HOUR_IN_MILLIS,
+ mQcConstants.HPJ_LIMIT_WORKING_MS - 10 * SECOND_IN_MILLIS, 1), true);
+
+ synchronized (mQuotaController.mLock) {
+ assertEquals(remainingTimeMs,
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ // Start the job.
+ mQuotaController.prepareForExecutionLocked(jobStatus);
+ }
+ advanceElapsedClock(remainingTimeMs);
+
+ // Wait for some extra time to allow for job processing.
+ verify(mJobSchedulerService,
+ timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
+ .onControllerStateChanged();
+ assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_HPJ_QUOTA));
+ // The job used up the remaining quota, but in that time, the same amount of time in the
+ // old TimingSession also fell out of the quota window, so it should still have the same
+ // amount of remaining time left its quota.
+ synchronized (mQuotaController.mLock) {
+ assertEquals(remainingTimeMs,
+ mQuotaController.getRemainingHpjExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ // Handler is told to check when the quota will be consumed, not when the initial
+ // remaining time is over.
+ verify(handler, atLeast(1)).sendMessageDelayed(
+ argThat(msg -> msg.what == QuotaController.MSG_REACHED_HPJ_QUOTA),
+ eq(10 * SECOND_IN_MILLIS));
+ verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs));
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
new file mode 100644
index 000000000000..edae08a3bfe1
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2020 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.pm
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.FallbackCategoryProvider
+import android.content.pm.FeatureInfo
+import android.content.pm.PackageParser.SigningDetails
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.content.pm.Signature
+import android.content.pm.UserInfo
+import android.content.pm.parsing.ParsingPackage
+import android.content.pm.parsing.ParsingPackageUtils
+import android.content.res.Resources
+import android.hardware.display.DisplayManager
+import android.os.Build
+import android.os.Environment
+import android.os.SystemProperties
+import android.os.UserHandle
+import android.os.UserManager
+import android.os.incremental.IncrementalManager
+import android.permission.IPermissionManager
+import android.util.ArrayMap
+import android.util.DisplayMetrics
+import android.util.EventLog
+import android.view.Display
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.any
+import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
+import com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt
+import com.android.dx.mockito.inline.extended.ExtendedMockito.anyString
+import com.android.dx.mockito.inline.extended.ExtendedMockito.argThat
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.eq
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spy
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder
+import com.android.internal.R
+import com.android.server.LocalServices
+import com.android.server.LockGuard
+import com.android.server.SystemConfig
+import com.android.server.SystemServerInitThreadPool
+import com.android.server.compat.PlatformCompat
+import com.android.server.extendedtestutils.wheneverStatic
+import com.android.server.pm.dex.DexManager
+import com.android.server.pm.parsing.PackageParser2
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.server.pm.parsing.pkg.ParsedPackage
+import com.android.server.pm.permission.PermissionManagerServiceInternal
+import com.android.server.testutils.mock
+import com.android.server.testutils.nullable
+import com.android.server.testutils.whenever
+import org.junit.Assert
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import org.mockito.AdditionalMatchers.or
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.quality.Strictness
+import java.io.File
+import java.io.IOException
+import java.nio.file.Files
+import java.security.PublicKey
+import java.security.cert.CertificateException
+import java.util.Arrays
+import java.util.Random
+import java.util.concurrent.FutureTask
+
+/**
+ * A utility for mocking behavior of the system and dependencies when testing PackageManagerService
+ *
+ * Create one of these and call [stageNominalSystemState] as a basis for additional behavior in most
+ * tests.
+ */
+class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
+ private val random = Random()
+ val mocks = Mocks()
+ val packageCacheDirectory: File =
+ Files.createTempDirectory("packageCache").toFile()
+ val rootDirectory: File =
+ Files.createTempDirectory("root").toFile()
+ val dataAppDirectory: File =
+ File(Files.createTempDirectory("data").toFile(), "app")
+ val frameworkSignature: SigningDetails = SigningDetails(arrayOf(generateSpySignature()), 3)
+ val systemPartitions: List<PackageManagerService.ScanPartition> =
+ redirectScanPartitions(PackageManagerService.SYSTEM_PARTITIONS)
+ val session: StaticMockitoSession
+
+ /** Tracks temporary files created by this class during the running of a test. */
+ private val createdFiles = ArrayList<File>()
+
+ /** Settings that are expected to be added as part of the test */
+ private val mPendingPackageAdds: MutableList<Pair<String, PackageSetting>> = ArrayList()
+
+ /** Settings simulated to be stored on disk */
+ private val mPreExistingSettings = ArrayMap<String, PackageSetting>()
+
+ /** The active map simulating the in memory storage of Settings */
+ private val mSettingsMap = ArrayMap<String, PackageSetting>()
+
+ init {
+ val apply = ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(SystemProperties::class.java)
+ .mockStatic(SystemConfig::class.java)
+ .mockStatic(SELinuxMMAC::class.java)
+ .mockStatic(FallbackCategoryProvider::class.java)
+ .mockStatic(PackageManagerServiceUtils::class.java)
+ .mockStatic(Environment::class.java)
+ .mockStatic(SystemServerInitThreadPool::class.java)
+ .mockStatic(ParsingPackageUtils::class.java)
+ .mockStatic(LockGuard::class.java)
+ .mockStatic(EventLog::class.java)
+ .mockStatic(LocalServices::class.java)
+ .apply(withSession)
+ session = apply.startMocking()
+ whenever(mocks.settings.insertPackageSettingLPw(
+ any(PackageSetting::class.java), any(AndroidPackage::class.java))) {
+ val name: String = (getArgument<Any>(0) as PackageSetting).name
+ val pendingAdd =
+ mPendingPackageAdds.firstOrNull { it.first == name } ?: return@whenever null
+ mPendingPackageAdds.remove(pendingAdd)
+ mSettingsMap[name] = pendingAdd.second
+ null
+ }
+ whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(),
+ nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(),
+ nullable(), nullable(), nullable())) {
+ val name: String = getArgument(0)
+ val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name }
+ ?: return@whenever null
+ mPendingPackageAdds.remove(pendingAdd)
+ mSettingsMap[name] = pendingAdd.second
+ pendingAdd.second
+ }
+ whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap)
+ whenever(mocks.settings.getPackageLPr(anyString())) { mSettingsMap[getArgument<Any>(0)] }
+ whenever(mocks.settings.readLPw(nullable())) {
+ mSettingsMap.putAll(mPreExistingSettings)
+ !mPreExistingSettings.isEmpty()
+ }
+ }
+
+ /** Collection of mocks used for PackageManagerService tests. */
+
+ class Mocks {
+ val lock = Any()
+ val installLock = Any()
+ val injector: PackageManagerService.Injector = mock()
+ val systemWrapper: PackageManagerService.SystemWrapper = mock()
+ val context: Context = mock()
+ val userManagerService: UserManagerService = mock()
+ val componentResolver: ComponentResolver = mock()
+ val permissionManagerInternal: PermissionManagerServiceInternal = mock()
+ val incrementalManager: IncrementalManager = mock()
+ val platformCompat: PlatformCompat = mock()
+ val settings: Settings = mock()
+ val resources: Resources = mock()
+ val systemConfig: SystemConfig = mock()
+ val apexManager: ApexManager = mock()
+ val userManagerInternal: UserManagerInternal = mock()
+ val packageParser: PackageParser2 = mock()
+ val keySetManagerService: KeySetManagerService = mock()
+ val packageAbiHelper: PackageAbiHelper = mock()
+ val appsFilter: AppsFilter = mock()
+ val dexManager: DexManager = mock()
+ val installer: Installer = mock()
+ val displayMetrics: DisplayMetrics = mock()
+ val permissionManager: IPermissionManager = mock()
+ }
+
+ companion object {
+ private const val DEVICE_PROVISIONING_PACKAGE_NAME =
+ "com.example.android.device.provisioning"
+ private val DEFAULT_AVAILABLE_FEATURES_MAP = ArrayMap<String, FeatureInfo>()
+ private val DEFAULT_ACTIVE_APEX_INFO_LIST = emptyList<ApexManager.ActiveApexInfo>()
+ private val DEFAULT_SHARED_LIBRARIES_LIST =
+ ArrayMap<String, SystemConfig.SharedLibraryEntry>()
+ private val DEFAULT_USERS = Arrays.asList(
+ UserInfo(UserHandle.USER_SYSTEM, "primary", "",
+ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_SYSTEM or UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM))
+ public val DEFAULT_VERSION_INFO = Settings.VersionInfo()
+
+ init {
+ DEFAULT_VERSION_INFO.fingerprint = "abcdef"
+ DEFAULT_VERSION_INFO.sdkVersion = Build.VERSION_CODES.R
+ DEFAULT_VERSION_INFO.databaseVersion = Settings.CURRENT_DATABASE_VERSION
+ }
+ }
+
+ /**
+ * Clean up any potentially dangling state. This should be run at the end of every test to
+ * account for changes to static memory, such as [LocalServices]
+ */
+ fun cleanup() {
+ createdFiles.forEach(File::delete)
+ createdFiles.clear()
+ mSettingsMap.clear()
+ mPendingPackageAdds.clear()
+ mPreExistingSettings.clear()
+ session.finishMocking()
+ }
+
+ /**
+ * Run this method to ensure that all expected actions were executed, such as pending
+ * [Settings] adds.
+ */
+ fun validateFinalState() {
+ if (mPendingPackageAdds.isNotEmpty()) {
+ Assert.fail(
+ "Not all expected settings were added: ${mPendingPackageAdds.map { it.first }}")
+ }
+ }
+
+ /**
+ * This method stages enough of system startup to execute the PackageManagerService constructor
+ * successfullly.
+ */
+ @Throws(Exception::class)
+ fun stageNominalSystemState() {
+ whenever(mocks.injector.context).thenReturn(mocks.context)
+ whenever(mocks.injector.lock).thenReturn(mocks.lock)
+ whenever(mocks.injector.installLock).thenReturn(mocks.installLock)
+ whenever(mocks.injector.systemWrapper).thenReturn(mocks.systemWrapper)
+ whenever(mocks.injector.userManagerService).thenReturn(mocks.userManagerService)
+ whenever(mocks.injector.componentResolver).thenReturn(mocks.componentResolver)
+ whenever(mocks.injector.permissionManagerServiceInternal) {
+ mocks.permissionManagerInternal
+ }
+ whenever(mocks.injector.permissionManagerService).thenReturn(mocks.permissionManager)
+ whenever(mocks.injector.incrementalManager).thenReturn(mocks.incrementalManager)
+ whenever(mocks.injector.compatibility).thenReturn(mocks.platformCompat)
+ whenever(mocks.injector.settings).thenReturn(mocks.settings)
+ whenever(mocks.injector.dexManager).thenReturn(mocks.dexManager)
+ whenever(mocks.injector.systemConfig).thenReturn(mocks.systemConfig)
+ whenever(mocks.injector.apexManager).thenReturn(mocks.apexManager)
+ whenever(mocks.injector.scanningCachingPackageParser).thenReturn(mocks.packageParser)
+ whenever(mocks.injector.scanningPackageParser).thenReturn(mocks.packageParser)
+ whenever(mocks.injector.systemPartitions).thenReturn(systemPartitions)
+ whenever(mocks.injector.appsFilter).thenReturn(mocks.appsFilter)
+ whenever(mocks.injector.abiHelper).thenReturn(mocks.packageAbiHelper)
+ whenever(mocks.injector.userManagerInternal).thenReturn(mocks.userManagerInternal)
+ whenever(mocks.injector.installer).thenReturn(mocks.installer)
+ whenever(mocks.injector.displayMetrics).thenReturn(mocks.displayMetrics)
+ wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig)
+ whenever(mocks.systemConfig.availableFeatures).thenReturn(DEFAULT_AVAILABLE_FEATURES_MAP)
+ whenever(mocks.systemConfig.sharedLibraries).thenReturn(DEFAULT_SHARED_LIBRARIES_LIST)
+ wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true)
+ wheneverStatic { Environment.getPackageCacheDirectory() }.thenReturn(packageCacheDirectory)
+ wheneverStatic { SystemProperties.digestOf("ro.build.fingerprint") }.thenReturn("cacheName")
+ wheneverStatic { Environment.getRootDirectory() }.thenReturn(rootDirectory)
+ wheneverStatic { SystemServerInitThreadPool.submit(any(Runnable::class.java), anyString())}
+ .thenAnswer { FutureTask<Any?>(it.getArgument(0), null) }
+
+ wheneverStatic { Environment.getDataDirectory() }.thenReturn(dataAppDirectory.parentFile)
+ wheneverStatic { Environment.getDataSystemDirectory() }
+ .thenReturn(File(dataAppDirectory.parentFile, "system"))
+ whenever(mocks.context.resources).thenReturn(mocks.resources)
+ whenever(mocks.resources.getString(R.string.config_deviceProvisioningPackage)) {
+ DEVICE_PROVISIONING_PACKAGE_NAME
+ }
+ whenever(mocks.apexManager.activeApexInfos).thenReturn(DEFAULT_ACTIVE_APEX_INFO_LIST)
+ whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap)
+ whenever(mocks.settings.internalVersion).thenReturn(DEFAULT_VERSION_INFO)
+ whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService)
+ whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService)
+ whenever(mocks.packageAbiHelper.derivePackageAbi(
+ any(AndroidPackage::class.java), anyBoolean(), nullable(), any(File::class.java))) {
+ android.util.Pair(PackageAbiHelper.Abis("", ""),
+ PackageAbiHelper.NativeLibraryPaths("", false, "", ""))
+ }
+ whenever(mocks.userManagerInternal.getUsers(true, false, false)).thenReturn(DEFAULT_USERS)
+ whenever(mocks.userManagerService.userIds).thenReturn(intArrayOf(0))
+ whenever(mocks.userManagerService.exists(0)).thenReturn(true)
+ whenever(mocks.packageAbiHelper.deriveNativeLibraryPaths(
+ any(AndroidPackage::class.java), anyBoolean(), any(File::class.java))) {
+ PackageAbiHelper.NativeLibraryPaths("", false, "", "")
+ }
+ // everything visible by default
+ whenever(mocks.appsFilter.shouldFilterApplication(
+ anyInt(), nullable(), nullable(), anyInt())) { false }
+
+ val displayManager: DisplayManager = mock()
+ whenever(mocks.context.getSystemService(DisplayManager::class.java))
+ .thenReturn(displayManager)
+ val display: Display = mock()
+ whenever(displayManager.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(display)
+
+ stageFrameworkScan()
+ stageInstallerScan()
+ stageServicesExtensionScan()
+ stageSystemSharedLibraryScan()
+ stagePermissionsControllerScan()
+ stageInstantAppResolverScan()
+ }
+
+ /**
+ * This method will stage the parsing and scanning of a package as well as add it to the
+ * [PackageSetting]s read from disk.
+ */
+ @Throws(Exception::class)
+ fun stageScanExistingPackage(
+ packageName: String,
+ versionCode: Long,
+ parent: File?,
+ withPackage: (PackageImpl) -> PackageImpl = { it },
+ withSetting:
+ (PackageSettingBuilder) -> PackageSettingBuilder = { it },
+ withExistingSetting:
+ (PackageSettingBuilder) -> PackageSettingBuilder = { it }
+ ) {
+ val existingSettingBuilderRef = arrayOfNulls<PackageSettingBuilder>(1)
+ stageScanNewPackage(packageName, versionCode, parent, withPackage,
+ withSetting = { settingBuilder ->
+ withSetting(settingBuilder)
+ existingSettingBuilderRef[0] = settingBuilder
+ settingBuilder
+ })
+ existingSettingBuilderRef[0]?.setPackage(null)
+ val packageSetting = existingSettingBuilderRef[0]?.let { withExistingSetting(it) }!!.build()
+ addPreExistingSetting(packageName, packageSetting)
+ }
+
+ /**
+ * This method will stage a [PackageSetting] read from disk, but does not stage any scanning
+ * or parsing of the package.
+ */
+ fun addPreExistingSetting(packageName: String, packageSetting: PackageSetting) {
+ mPreExistingSettings[packageName] = packageSetting
+ }
+
+ /**
+ * This method will stage the parsing and scanning of a package but will not add it to the set
+ * of [PackageSetting]s read from disk.
+ */
+ @Throws(Exception::class)
+ fun stageScanNewPackage(
+ packageName: String,
+ versionCode: Long,
+ parent: File?,
+ withPackage: (PackageImpl) -> PackageImpl = { it },
+ withSetting: (PackageSettingBuilder) -> PackageSettingBuilder = { it }
+ ) {
+ val pair = createBasicAndroidPackage(parent, packageName, versionCode)
+ val apkPath = pair.first
+ val pkg = withPackage(pair.second)
+ stageParse(apkPath, pkg)
+ val parentFile = apkPath.parentFile
+ val settingBuilder = withSetting(createBasicSettingBuilder(parentFile, pkg))
+ stageSettingInsert(packageName, settingBuilder.build())
+ }
+
+ /**
+ * Creates a simple package that should reasonably parse for scan operations. This can be used
+ * as a basis for more complicated packages.
+ */
+ fun createBasicAndroidPackage(
+ parent: File?,
+ packageName: String,
+ versionCode: Long,
+ signingDetails: SigningDetails =
+ createRandomSigningDetails()
+ ): Pair<File, PackageImpl> {
+ val apkPath = File(File(parent, packageName), "base.apk")
+ val pkg = PackageImpl.forTesting(packageName, apkPath.parentFile.path) as PackageImpl
+ pkg.signingDetails = signingDetails
+ wheneverStatic { ParsingPackageUtils.getSigningDetails(eq(pkg), anyBoolean()) }
+ .thenReturn(signingDetails)
+ pkg.versionCode = versionCode.toInt()
+ pkg.versionCodeMajor = (versionCode shr 32).toInt()
+ pkg.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
+ return Pair(apkPath, pkg)
+ }
+
+ /**
+ * This method will create a spy of a [SigningDetails] object to be used when simulating the
+ * collection of signatures.
+ */
+ fun createRandomSigningDetails(): SigningDetails {
+ val signingDetails = spy(SigningDetails(arrayOf(generateSpySignature()),
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3))
+ doReturn(true).whenever(signingDetails).checkCapability(
+ anyString(), anyInt())
+ doReturn(true).whenever(signingDetails).checkCapability(
+ any(SigningDetails::class.java), anyInt())
+ return signingDetails
+ }
+
+ /**
+ * This method will create a basic [PackageSettingBuilder] from an [AndroidPackage] with all of
+ * the necessary parameters to be returned by a simple scan. This can be used as a basis for
+ * more complicated settings.
+ */
+ fun createBasicSettingBuilder(parentFile: File, pkg: AndroidPackage): PackageSettingBuilder {
+ return createBasicSettingBuilder(parentFile, pkg.packageName, pkg.longVersionCode,
+ pkg.signingDetails)
+ .setPackage(pkg)
+ }
+
+ /**
+ * This method will create a basic [PackageSettingBuilder] with all of the necessary parameters
+ * to be returned by a simple scan. This can be used as a basis for more complicated settings.
+ */
+ fun createBasicSettingBuilder(
+ parentFile: File,
+ packageName: String,
+ versionCode: Long,
+ signingDetails: SigningDetails
+ ): PackageSettingBuilder {
+ return PackageSettingBuilder()
+ .setCodePath(parentFile.path)
+ .setName(packageName)
+ .setPVersionCode(versionCode)
+ .setSigningDetails(signingDetails)
+ }
+
+ fun createBasicApplicationInfo(pkg: ParsingPackage): ApplicationInfo {
+ val applicationInfo: ApplicationInfo = mock()
+ applicationInfo.packageName = pkg.packageName
+ return applicationInfo
+ }
+
+ fun createBasicActivityInfo(
+ pkg: ParsingPackage,
+ applicationInfo: ApplicationInfo?,
+ className: String?
+ ):
+ ActivityInfo {
+ val activityInfo = ActivityInfo()
+ activityInfo.applicationInfo = applicationInfo
+ activityInfo.packageName = pkg.packageName
+ activityInfo.name = className
+ return activityInfo
+ }
+
+ fun createBasicServiceInfo(
+ pkg: ParsingPackage,
+ applicationInfo: ApplicationInfo?,
+ className: String?
+ ):
+ ServiceInfo {
+ val serviceInfo = ServiceInfo()
+ serviceInfo.applicationInfo = applicationInfo
+ serviceInfo.packageName = pkg.packageName
+ serviceInfo.name = className
+ return serviceInfo
+ }
+
+ /** Finds the appropriate partition, if available, based on a scan flag unique to it. */
+ fun getPartitionFromFlag(scanFlagMask: Int): PackageManagerService.ScanPartition =
+ systemPartitions.first { (it.scanFlag and scanFlagMask) != 0 }
+
+ @Throws(Exception::class)
+ private fun stageParse(path: File, parseResult: ParsingPackage): ParsedPackage {
+ val basePath = path.parentFile
+ basePath.mkdirs()
+ path.createNewFile()
+ createdFiles.add(path)
+ val parsedPackage = parseResult.hideAsParsed() as ParsedPackage
+ whenever(mocks.packageParser.parsePackage(
+ or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage }
+ return parsedPackage
+ }
+
+ private fun stageSettingInsert(name: String, setting: PackageSetting): PackageSetting {
+ mPendingPackageAdds.add(Pair(name, setting))
+ return setting
+ }
+
+ @Throws(Exception::class)
+ private fun stageFrameworkScan() {
+ val apk = File(File(rootDirectory, "framework"), "framework-res.apk")
+ val frameworkPkg = PackageImpl.forTesting("android",
+ apk.parentFile.path) as PackageImpl
+ wheneverStatic { ParsingPackageUtils.getSigningDetails(frameworkPkg, true) }
+ .thenReturn(frameworkSignature)
+ stageParse(apk, frameworkPkg)
+ stageSettingInsert("android",
+ PackageSettingBuilder().setCodePath(apk.path).setName(
+ "android").setPackage(frameworkPkg).build())
+ }
+
+ @Throws(Exception::class)
+ private fun stageInstantAppResolverScan() {
+ whenever(mocks.resources.getStringArray(R.array.config_ephemeralResolverPackage)) {
+ arrayOf("com.android.test.ephemeral.resolver")
+ }
+ stageScanNewPackage("com.android.test.ephemeral.resolver",
+ 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder,
+ withPackage = { pkg: PackageImpl ->
+ val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg)
+ whenever(applicationInfo.isPrivilegedApp).thenReturn(true)
+ mockQueryServices(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE,
+ createBasicServiceInfo(pkg, applicationInfo, "test.EphemeralService"))
+ mockQueryActivities(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS,
+ createBasicActivityInfo(pkg, applicationInfo, "test.SettingsActivity"))
+ pkg
+ },
+ withSetting = { setting: PackageSettingBuilder ->
+ setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+ })
+ }
+
+ @Throws(Exception::class)
+ private fun stagePermissionsControllerScan() {
+ stageScanNewPackage("com.android.permissions.controller",
+ 1L, systemPartitions[0].privAppFolder,
+ withPackage = { pkg: PackageImpl ->
+ val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg)
+ whenever(applicationInfo.isPrivilegedApp).thenReturn(true)
+ mockQueryActivities(Intent.ACTION_MANAGE_PERMISSIONS,
+ createBasicActivityInfo(
+ pkg, applicationInfo, "test.PermissionActivity"))
+ pkg
+ },
+ withSetting = { setting: PackageSettingBuilder ->
+ setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+ })
+ }
+
+ @Throws(Exception::class)
+ private fun stageSystemSharedLibraryScan() {
+ stageScanNewPackage("android.ext.shared",
+ 1L, systemPartitions[0].appFolder,
+ withPackage = { it.addLibraryName("android.ext.shared") as PackageImpl },
+ withSetting = { setting: PackageSettingBuilder ->
+ setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+ }
+ )
+ }
+
+ @Throws(Exception::class)
+ private fun stageServicesExtensionScan() {
+ whenever(mocks.context.getString(R.string.config_servicesExtensionPackage)) {
+ "com.android.test.services.extension"
+ }
+ stageScanNewPackage("com.android.test.services.extension",
+ 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_SYSTEM_EXT).privAppFolder,
+ withSetting = { setting: PackageSettingBuilder ->
+ setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+ })
+ }
+
+ @Throws(Exception::class)
+ private fun stageInstallerScan() {
+ stageScanNewPackage(
+ "com.android.test.installer",
+ 1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder,
+ withPackage = { pkg: PackageImpl ->
+ val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg)
+ whenever(applicationInfo.isPrivilegedApp).thenReturn(true)
+ val installerActivity: ActivityInfo = createBasicActivityInfo(
+ pkg, applicationInfo, "test.InstallerActivity")
+ mockQueryActivities(Intent.ACTION_INSTALL_PACKAGE, installerActivity)
+ mockQueryActivities(Intent.ACTION_UNINSTALL_PACKAGE, installerActivity)
+ mockQueryActivities(Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE,
+ installerActivity)
+ pkg
+ },
+ withSetting = { setting: PackageSettingBuilder ->
+ setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+ }
+ )
+ }
+
+ private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) {
+ whenever(mocks.componentResolver.queryActivities(
+ argThat { intent: Intent? -> intent != null && (action == intent.action) },
+ nullable(), anyInt(), anyInt())) {
+ ArrayList(activities.asList().map { info: ActivityInfo? ->
+ ResolveInfo().apply { activityInfo = info }
+ })
+ }
+ }
+
+ private fun mockQueryServices(action: String, vararg services: ServiceInfo) {
+ whenever(mocks.componentResolver.queryServices(
+ argThat { intent: Intent? -> intent != null && (action == intent.action) },
+ nullable(), anyInt(), anyInt())) {
+ ArrayList(services.asList().map { info ->
+ ResolveInfo().apply { serviceInfo = info }
+ })
+ }
+ }
+
+ fun generateSpySignature(): Signature {
+ val bytes = ByteArray(32)
+ random.nextBytes(bytes)
+ val signature = spy(Signature(bytes))
+ try {
+ val mockPublicKey: PublicKey = mock()
+ doReturn(mockPublicKey).whenever(signature).getPublicKey()
+ } catch (e: CertificateException) {
+ throw RuntimeException(e)
+ }
+ return signature
+ }
+
+ /** Override get*Folder methods to point to temporary local directories */
+
+ @Throws(IOException::class)
+ private fun redirectScanPartitions(partitions: List<PackageManagerService.ScanPartition>):
+ List<PackageManagerService.ScanPartition> {
+ val spiedPartitions: MutableList<PackageManagerService.ScanPartition> =
+ ArrayList(partitions.size)
+ for (partition: PackageManagerService.ScanPartition in partitions) {
+ val spy = spy(partition)
+ val newRoot = Files.createTempDirectory(partition.folder.name).toFile()
+ whenever(spy.overlayFolder).thenReturn(File(newRoot, "overlay"))
+ whenever(spy.appFolder).thenReturn(File(newRoot, "app"))
+ whenever(spy.privAppFolder).thenReturn(File(newRoot, "priv-app"))
+ whenever(spy.folder).thenReturn(newRoot)
+ spiedPartitions.add(spy)
+ }
+ return spiedPartitions
+ }
+}
+
+/**
+ * Sets up a basic [MockSystem] for use in a test method. This will create a MockSystem before the
+ * test method and any [org.junit.Before] annotated methods. It can then be used to access the
+ * MockSystem via the [system] method or the mocks directly via [mocks].
+ */
+class MockSystemRule : TestRule {
+ var mockSystem: MockSystem? = null
+ override fun apply(base: Statement?, description: Description?) = object : Statement() {
+ @Throws(Throwable::class)
+ override fun evaluate() {
+ mockSystem = MockSystem()
+ try {
+ base!!.evaluate()
+ } finally {
+ mockSystem?.cleanup()
+ mockSystem = null
+ }
+ }
+ }
+
+ /** Fetch the [MockSystem] instance prepared for this test */
+ fun system(): MockSystem = mockSystem!!
+ /** Fetch the [MockSystem.Mocks] prepared for this test */
+ fun mocks(): MockSystem.Mocks = mockSystem!!.mocks
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
new file mode 100644
index 000000000000..bd44c360b518
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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.pm
+
+import android.content.pm.ApplicationInfo.FLAG_SYSTEM
+import android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
+import android.content.pm.PackageManager
+import android.content.pm.PackageParser
+import android.os.Build
+import android.os.Process
+import android.util.Log
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.whenever
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
+import org.hamcrest.collection.IsMapContaining.hasKey
+import org.hamcrest.core.IsNot.not
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import java.io.File
+
+@RunWith(JUnit4::class)
+class PackageManagerServiceBootTest {
+
+ @Rule
+ @JvmField
+ val rule = MockSystemRule()
+
+ @Before
+ @Throws(Exception::class)
+ fun setup() {
+ Log.i("system.out", "setup", Exception())
+ rule.system().stageNominalSystemState()
+ }
+
+ private fun createPackageManagerService(): PackageManagerService {
+ return PackageManagerService(rule.mocks().injector,
+ false /*coreOnly*/,
+ false /*factoryTest*/,
+ MockSystem.DEFAULT_VERSION_INFO.fingerprint,
+ false /*isEngBuild*/,
+ false /*isUserDebugBuild*/,
+ Build.VERSION_CODES.CUR_DEVELOPMENT,
+ Build.VERSION.INCREMENTAL)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun simpleConstruction() {
+ val pm = createPackageManagerService()
+ verify(rule.mocks().injector).bootstrap(pm)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.system",
+ Process.SYSTEM_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.phone",
+ Process.PHONE_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.log",
+ Process.LOG_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.nfc",
+ Process.NFC_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.bluetooth",
+ Process.BLUETOOTH_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.shell",
+ Process.SHELL_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.se",
+ Process.SE_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ verify(rule.mocks().settings).addSharedUserLPw("android.uid.networkstack",
+ Process.NETWORK_STACK_UID, FLAG_SYSTEM, PRIVATE_FLAG_PRIVILEGED)
+ rule.system().validateFinalState()
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun existingDataPackage_remains() {
+ rule.system().stageScanExistingPackage("a.data.package", 1L, rule.system().dataAppDirectory)
+ val pm = createPackageManagerService()
+ rule.system().validateFinalState()
+ assertThat(pm.mPackages, hasKey("a.data.package"))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun unexpectedDataPackage_isRemoved() {
+ rule.system().stageScanNewPackage(
+ "a.data.package", 1L, rule.system().dataAppDirectory)
+ val pm = createPackageManagerService()
+ verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw(
+ argThat { setting: PackageSetting -> setting.name == "a.data.package" },
+ argThat { pkg: AndroidPackage -> pkg.packageName == "a.data.package" })
+ assertThat(pm.mPackages, not(hasKey("a.data.package")))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun expectedPackageMissing_doesNotReplace() {
+ // setup existing package
+ rule.system().stageScanExistingPackage("a.data.package", 1L,
+ rule.system().dataAppDirectory)
+ // simulate parsing failure for any path containing the package name.
+ whenever(rule.mocks().packageParser.parsePackage(
+ argThat { path: File -> path.path.contains("a.data.package") },
+ anyInt(),
+ anyBoolean()))
+ .thenThrow(PackageParser.PackageParserException(
+ PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
+ val pm = createPackageManagerService()
+ verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw(
+ argThat { setting: PackageSetting -> setting.name == "a.data.package" },
+ argThat { pkg: AndroidPackage -> pkg.packageName == "a.data.package" })
+ assertThat(pm.mPackages, not(hasKey("a.data.package")))
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun expectingBetter_updateStillBetter() {
+ // Debug.waitForDebugger()
+ val systemAppPackageName = "com.android.test.updated.system.app"
+ val systemAppSigningDetails = rule.system().createRandomSigningDetails()
+ val systemVersionParent = rule.system()
+ .getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder
+
+ // system app v1 is disabled
+ whenever(rule.mocks().settings.isDisabledSystemPackageLPr(systemAppPackageName)) {
+ true
+ }
+ whenever(rule.mocks().settings.getDisabledSystemPkgLPr(systemAppPackageName)) {
+ rule.system().createBasicSettingBuilder(
+ File(systemVersionParent, systemAppPackageName),
+ systemAppPackageName, 1, systemAppSigningDetails).build()
+ }
+
+ // system app v3 is on data/app
+ rule.system().stageScanExistingPackage(
+ systemAppPackageName,
+ 3,
+ rule.system().dataAppDirectory,
+ withPackage = { it.apply { signingDetails = systemAppSigningDetails } },
+ withExistingSetting = { it.setPkgFlags(FLAG_SYSTEM) })
+
+ // system app v2 is scanned from system
+ rule.system().stageScanNewPackage(systemAppPackageName, 2, systemVersionParent,
+ withPackage = { it.apply { signingDetails = systemAppSigningDetails } },
+ withSetting = { it.setPkgFlags(FLAG_SYSTEM) })
+
+ val pm = createPackageManagerService()
+
+ assertThat("system package should exist after boot",
+ pm.mPackages[systemAppPackageName], notNullValue())
+ assertThat("system package should remain at version on data/app",
+ pm.mPackages[systemAppPackageName]!!.longVersionCode, equalTo(3))
+ }
+} \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java
new file mode 100644
index 000000000000..df533f3c122a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/MultiRateLimiterTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2020 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.utils.quota;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.TestableContext;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.time.Duration;
+
+@SmallTest
+public class MultiRateLimiterTest {
+
+ private static final int USER_ID = 1;
+ private static final String PACKAGE_NAME_1 = "com.android.package.one";
+ private static final String PACKAGE_NAME_2 = "com.android.package.two";
+ private static final String TAG = "tag";
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ private final InjectorForTest mInjector = new InjectorForTest();
+
+ private static class InjectorForTest extends QuotaTracker.Injector {
+ Duration mElapsedTime = Duration.ZERO;
+
+ @Override
+ public long getElapsedRealtime() {
+ return mElapsedTime.toMillis();
+ }
+
+ @Override
+ public boolean isAlarmManagerReady() {
+ return true;
+ }
+ }
+
+ @Test
+ public void testSingleRateLimit_belowLimit_isWithinQuota() {
+ MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
+ .addRateLimit(3, Duration.ofSeconds(20))
+ .build();
+
+ // Three quick events are within quota.
+ mInjector.mElapsedTime = Duration.ZERO;
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(50);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(100);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ }
+
+ @Test
+ public void testSingleRateLimit_aboveLimit_isNotWithinQuota() {
+ MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
+ .addRateLimit(3, Duration.ofSeconds(20))
+ .build();
+
+ mInjector.mElapsedTime = Duration.ZERO;
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(50);
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(100);
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(150);
+ // We hit the limit, 4th event in under 20 seconds is not within quota.
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+ }
+
+ @Test
+ public void testSingleRateLimit_afterGoingAboveQuotaAndWaitingWindow_isBackWithinQuota() {
+ MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
+ .addRateLimit(3, Duration.ofSeconds(20))
+ .build();
+
+ mInjector.mElapsedTime = Duration.ZERO;
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(50);
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(100);
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(150);
+ // We hit the limit, 4th event in under 20 seconds is not within quota.
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+
+ mInjector.mElapsedTime = Duration.ofSeconds(21);
+ // 20 seconds have passed, we're again within quota.
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ }
+
+ @Test
+ public void createMultipleRateLimits_testTheyLimitsAsExpected() {
+ MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
+ .addRateLimit(3, Duration.ofSeconds(20)) // 1st limit
+ .addRateLimit(4, Duration.ofSeconds(40)) // 2nd limit
+ .addRateLimit(5, Duration.ofSeconds(60)) // 3rd limit
+ .build();
+
+ // Testing the 1st limit
+ mInjector.mElapsedTime = Duration.ZERO;
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(50);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(100);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(150);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+
+ mInjector.mElapsedTime = Duration.ofSeconds(21);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ // Testing the 2nd limit
+ mInjector.mElapsedTime = Duration.ofSeconds(35);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+
+ mInjector.mElapsedTime = Duration.ofSeconds(42);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ // Testing the 3rd limit.
+ mInjector.mElapsedTime = Duration.ofSeconds(43);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+
+ mInjector.mElapsedTime = Duration.ofSeconds(62);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+ }
+
+ @Test
+ public void createSingleRateLimit_testItLimitsOnlyGivenUptc() {
+ MultiRateLimiter multiRateLimiter = new MultiRateLimiter.Builder(mContext, mInjector)
+ .addRateLimit(3, Duration.ofSeconds(20))
+ .build();
+
+ mInjector.mElapsedTime = Duration.ZERO;
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(50);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(100);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue();
+ multiRateLimiter.noteEvent(USER_ID, PACKAGE_NAME_1, TAG);
+
+ mInjector.mElapsedTime = Duration.ofMillis(150);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isFalse();
+ // Different userId - packageName - tag combination is still allowed.
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue();
+
+ mInjector.mElapsedTime = Duration.ofSeconds(21);
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_1, TAG)).isTrue();
+ assertThat(multiRateLimiter.isWithinQuota(USER_ID, PACKAGE_NAME_2, TAG)).isTrue();
+ }
+}
diff --git a/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt
new file mode 100644
index 000000000000..72ae77e48e25
--- /dev/null
+++ b/services/tests/mockingservicestests/utils-mockito/com/android/server/extendedtestutils/ExtendedMockitoUtils.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 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.extendedtestutils
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.MockedMethod
+import com.android.dx.mockito.inline.extended.StaticCapableStubber
+import org.mockito.stubbing.Answer
+
+fun <T> StaticCapableStubber.wheneverStatic(methodCall: MockedMethod<T>) {
+ this.`when`(methodCall)
+}
+
+fun <T> wheneverStatic(mockedMethod: MockedMethod<T>) = object : CustomStaticStubber<T> {
+ override fun thenAnswer(answer: Answer<T>) {
+ ExtendedMockito.doAnswer(answer).wheneverStatic(mockedMethod)
+ }
+
+ override fun thenReturn(value: T) {
+ ExtendedMockito.doReturn(value).wheneverStatic(mockedMethod)
+ }
+}
+
+interface CustomStaticStubber<T> {
+ fun thenAnswer(answer: Answer<T>)
+ fun thenReturn(value: T)
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 343b156e443a..6daa381f526e 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -33,6 +33,7 @@ android_test {
"androidx.test.ext.truth",
"androidx.test.runner",
"androidx.test.rules",
+ "platform-compat-test-rules",
"mockito-target-minus-junit4",
"platform-test-annotations",
"ShortcutManagerTestUtils",
@@ -111,6 +112,16 @@ android_test {
}
java_library {
+ name: "servicestests-core-utils",
+ srcs: [
+ "src/com/android/server/pm/PackageSettingBuilder.java",
+ ],
+ static_libs: [
+ "services.core",
+ ],
+}
+
+java_library {
name: "servicestests-utils",
srcs: [
"utils/**/*.java",
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
deleted file mode 100644
index 33ea1d6f829e..000000000000
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
-import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
-import static android.net.INetd.FIREWALL_RULE_ALLOW;
-import static android.net.INetd.FIREWALL_RULE_DENY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.util.DebugUtils.valueToString;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.net.NetworkPolicyManager;
-import android.util.ArrayMap;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.function.BiFunction;
-
-/**
- * Test class for {@link NetworkManagementInternal}.
- *
- * To run the tests, use
- *
- * runtest -c com.android.server.NetworkManagementInternalTest frameworks-services
- *
- * or the following steps:
- *
- * Build: m FrameworksServicesTests
- * Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- * Run: adb shell am instrument -e class com.android.server.NetworkManagementInternalTest -w \
- * com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NetworkManagementInternalTest {
- private static final int TEST_UID = 111;
-
- private NetworkManagementService.Injector mInjector;
- private NetworkManagementInternal mNmi;
-
- @Before
- public void setUp() {
- final NetworkManagementService service = new NetworkManagementService();
- mInjector = service.getInjector();
- mNmi = service.new LocalService();
- }
-
- @Test
- public void testIsNetworkRestrictedForUid() {
- // No firewall chains enabled
- assertFalse(mNmi.isNetworkRestrictedForUid(TEST_UID));
-
- // Restrict usage of mobile data in background
- mInjector.setUidOnMeteredNetworkList(true, TEST_UID, true);
- assertTrue("Should be true since mobile data usage is restricted",
- mNmi.isNetworkRestrictedForUid(TEST_UID));
- mInjector.reset();
-
- // Data saver is on and uid is not allowlisted
- mInjector.setDataSaverMode(true);
- mInjector.setUidOnMeteredNetworkList(false, TEST_UID, false);
- assertTrue("Should be true since data saver is on and the uid is not whitelisted",
- mNmi.isNetworkRestrictedForUid(TEST_UID));
- mInjector.reset();
-
- // Data saver is on and uid is allowlisted
- mInjector.setDataSaverMode(true);
- mInjector.setUidOnMeteredNetworkList(false, TEST_UID, true);
- assertFalse("Should be false since data saver is on and the uid is whitelisted",
- mNmi.isNetworkRestrictedForUid(TEST_UID));
- mInjector.reset();
-
- final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>();
- // Dozable chain
- final ArrayMap<Integer, Boolean> isRestrictedForDozable = new ArrayMap<>();
- isRestrictedForDozable.put(FIREWALL_RULE_DEFAULT, true);
- isRestrictedForDozable.put(FIREWALL_RULE_ALLOW, false);
- isRestrictedForDozable.put(FIREWALL_RULE_DENY, true);
- expected.put(FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable);
- // Powersaver chain
- final ArrayMap<Integer, Boolean> isRestrictedForPowerSave = new ArrayMap<>();
- isRestrictedForPowerSave.put(FIREWALL_RULE_DEFAULT, true);
- isRestrictedForPowerSave.put(FIREWALL_RULE_ALLOW, false);
- isRestrictedForPowerSave.put(FIREWALL_RULE_DENY, true);
- expected.put(FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave);
- // Standby chain
- final ArrayMap<Integer, Boolean> isRestrictedForStandby = new ArrayMap<>();
- isRestrictedForStandby.put(FIREWALL_RULE_DEFAULT, false);
- isRestrictedForStandby.put(FIREWALL_RULE_ALLOW, false);
- isRestrictedForStandby.put(FIREWALL_RULE_DENY, true);
- expected.put(FIREWALL_CHAIN_STANDBY, isRestrictedForStandby);
-
- final int[] chains = {
- FIREWALL_CHAIN_STANDBY,
- FIREWALL_CHAIN_POWERSAVE,
- FIREWALL_CHAIN_DOZABLE
- };
- final int[] states = {
- FIREWALL_RULE_ALLOW,
- FIREWALL_RULE_DENY,
- FIREWALL_RULE_DEFAULT
- };
- BiFunction<Integer, Integer, String> errorMsg = (chain, state) -> {
- return String.format("Unexpected value for chain: %s and state: %s",
- valueToString(NetworkPolicyManager.class, "FIREWALL_CHAIN_", chain),
- valueToString(NetworkPolicyManager.class, "FIREWALL_RULE_", state));
- };
- for (int chain : chains) {
- final ArrayMap<Integer, Boolean> expectedValues = expected.get(chain);
- mInjector.setFirewallChainState(chain, true);
- for (int state : states) {
- mInjector.setFirewallRule(chain, TEST_UID, state);
- assertEquals(errorMsg.apply(chain, state),
- expectedValues.get(state), mNmi.isNetworkRestrictedForUid(TEST_UID));
- }
- mInjector.reset();
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index e609adc2a067..ce3751abfed7 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -72,7 +72,7 @@ import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.devicepolicy.MockUtils;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.google.android.collect.Lists;
@@ -133,10 +133,10 @@ public class NetworkScoreServiceTest {
@Mock private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter;
@Mock private WifiInfo mWifiInfo;
@Mock private NetworkScoreService.ScoringServiceConnection mServiceConnection;
- @Mock private PermissionManagerServiceInternal mPermissionManagerInternal;
+ @Mock private LegacyPermissionManagerInternal mPermissionManagerInternal;
@Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor;
- @Captor private
- ArgumentCaptor<PermissionManagerServiceInternal.PackagesProvider> mPackagesProviderCaptor;
+ @Captor private ArgumentCaptor<LegacyPermissionManagerInternal.PackagesProvider>
+ mPackagesProviderCaptor;
private ContentResolver mContentResolver;
private NetworkScoreService mNetworkScoreService;
@@ -165,7 +165,7 @@ public class NetworkScoreServiceTest {
mHandlerThread = new HandlerThread("NetworkScoreServiceTest");
mHandlerThread.start();
LocalServices.addService(
- PermissionManagerServiceInternal.class, mPermissionManagerInternal);
+ LegacyPermissionManagerInternal.class, mPermissionManagerInternal);
mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager,
networkScorerAppData -> mServiceConnection, mHandlerThread.getLooper());
WifiConfiguration configuration = new WifiConfiguration();
@@ -191,7 +191,7 @@ public class NetworkScoreServiceTest {
@After
public void tearDown() throws Exception {
mHandlerThread.quitSafely();
- LocalServices.removeServiceForTest(PermissionManagerServiceInternal.class);
+ LocalServices.removeServiceForTest(LegacyPermissionManagerInternal.class);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 3f3d5e5106c5..839bc93ba00f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -35,6 +35,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.os.IBinder;
import android.os.UserHandle;
@@ -94,6 +95,8 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
@Mock private IAccessibilityServiceClient mMockServiceClient;
@Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
@Mock private MagnificationController mMockMagnificationController;
+ @Mock private Resources mMockResources;
+
private AccessibilityUserState mUserState;
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
@@ -121,6 +124,9 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
mMockA11yDisplayListener,
mMockMagnificationController);
+ mMockResources = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+
final AccessibilityUserState userState = new AccessibilityUserState(
mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms);
mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), userState);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index fbfb0455bd46..81ca92cc3c8c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -42,11 +42,16 @@ import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.ArraySet;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import org.junit.After;
@@ -90,8 +95,13 @@ public class AccessibilityUserStateTest {
private AccessibilityUserState mUserState;
+ private int mFocusStrokeWidthDefaultValue;
+ private int mFocusColorDefaultValue;
+
@Before
public void setUp() {
+ final Resources resources = InstrumentationRegistry.getContext().getResources();
+
MockitoAnnotations.initMocks(this);
FakeSettingsProvider.clearSettingsProvider();
mMockResolver = new MockContentResolver();
@@ -99,6 +109,11 @@ public class AccessibilityUserStateTest {
when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME);
when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo);
+ when(mMockContext.getResources()).thenReturn(resources);
+
+ mFocusStrokeWidthDefaultValue =
+ resources.getDimensionPixelSize(R.dimen.accessibility_focus_highlight_stroke_width);
+ mFocusColorDefaultValue = resources.getColor(R.color.accessibility_focus_highlight_color);
mUserState = new AccessibilityUserState(USER_ID, mMockContext, mMockListener);
}
@@ -129,6 +144,7 @@ public class AccessibilityUserStateTest {
mUserState.setUserNonInteractiveUiTimeoutLocked(30);
mUserState.setUserInteractiveUiTimeoutLocked(30);
mUserState.setMagnificationModeLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ mUserState.setFocusAppearanceLocked(20, Color.BLUE);
mUserState.onSwitchToAnotherUserLocked();
@@ -150,6 +166,8 @@ public class AccessibilityUserStateTest {
assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
mUserState.getMagnificationModeLocked());
+ assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked());
+ assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked());
}
@Test
@@ -350,6 +368,21 @@ public class AccessibilityUserStateTest {
mUserState.getMagnificationModeLocked());
}
+ @Test
+ public void setFocusAppearanceData_returnExpectedFocusAppearanceData() {
+ final int focusStrokeWidthValue = 100;
+ final int focusColorValue = Color.BLUE;
+
+ assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked());
+ assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked());
+
+ mUserState.setFocusAppearanceLocked(focusStrokeWidthValue, focusColorValue);
+
+ assertEquals(focusStrokeWidthValue, mUserState.getFocusStrokeWidthLocked());
+ assertEquals(focusColorValue, mUserState.getFocusColorLocked());
+
+ }
+
private int getSecureIntForUser(String key, int userId) {
return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index b929061a967e..f38def8c3c77 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -37,6 +37,7 @@ import com.android.server.appsearch.proto.SearchSpecProto;
import com.android.server.appsearch.proto.StringIndexingConfig;
import com.android.server.appsearch.proto.TermMatchType;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
@@ -46,9 +47,7 @@ import org.junit.rules.TemporaryFolder;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
public class AppSearchImplTest {
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
@@ -66,14 +65,15 @@ public class AppSearchImplTest {
+ VisibilityStore.SCHEMA_TYPE)
.addProperty(
new AppSearchSchema.PropertyConfig.Builder(
- VisibilityStore.PLATFORM_HIDDEN_PROPERTY)
+ VisibilityStore.NOT_PLATFORM_SURFACEABLE_PROPERTY)
.setDataType(
AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.build())
.build();
- mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema);
+ mVisibilitySchemaProto =
+ SchemaToProtoConverter.toSchemaTypeConfigProto(visibilityAppSearchSchema);
}
/**
@@ -340,9 +340,13 @@ public class AppSearchImplTest {
@Test
public void testOptimize() throws Exception {
// Insert schema
- Set<AppSearchSchema> schemas =
- Collections.singleton(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false);
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ "database",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
// Insert enough documents.
for (int i = 0;
@@ -351,7 +355,7 @@ public class AppSearchImplTest {
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
GenericDocument document =
- new GenericDocument.Builder("uri" + i, "type")
+ new GenericDocument.Builder<>("uri" + i, "type")
.setNamespace("namespace")
.build();
mAppSearchImpl.putDocument("database", document);
@@ -392,13 +396,17 @@ public class AppSearchImplTest {
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
// Insert schema
- Set<AppSearchSchema> schemas =
- Collections.singleton(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false);
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ "database",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
// Insert document
GenericDocument document =
- new GenericDocument.Builder("uri", "type").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build();
mAppSearchImpl.putDocument("database", document);
// Rewrite SearchSpec
@@ -413,20 +421,28 @@ public class AppSearchImplTest {
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
// Insert schema
- Set<AppSearchSchema> schemas =
- Set.of(
+ List<AppSearchSchema> schemas =
+ ImmutableList.of(
new AppSearchSchema.Builder("typeA").build(),
new AppSearchSchema.Builder("typeB").build());
- mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
- mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false);
+ mAppSearchImpl.setSchema(
+ "database1",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
+ mAppSearchImpl.setSchema(
+ "database2",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
// Insert documents
GenericDocument document1 =
- new GenericDocument.Builder("uri", "typeA").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build();
mAppSearchImpl.putDocument("database1", document1);
GenericDocument document2 =
- new GenericDocument.Builder("uri", "typeB").setNamespace("namespace").build();
+ new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build();
mAppSearchImpl.putDocument("database2", document2);
// Rewrite SearchSpec
@@ -477,10 +493,14 @@ public class AppSearchImplTest {
@Test
public void testSetSchema() throws Exception {
- Set<AppSearchSchema> schemas =
- Collections.singleton(new AppSearchSchema.Builder("Email").build());
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("Email").build());
// Set schema Email to AppSearch database1
- mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
+ mAppSearchImpl.setSchema(
+ "database1",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto =
@@ -500,35 +520,47 @@ public class AppSearchImplTest {
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
mAppSearchImpl.setSchema(
"database",
- Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*forceOverride=*/ false);
- mAppSearchImpl.setVisibility("database", Set.of("schema1"));
// "schema1" is platform hidden now
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .getSchemasNotPlatformSurfaceable("database"))
.containsExactly("database/schema1");
// Add a new schema, and include the already-existing "schema1"
mAppSearchImpl.setSchema(
"database",
- Set.of(
+ ImmutableList.of(
new AppSearchSchema.Builder("schema1").build(),
new AppSearchSchema.Builder("schema2").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*forceOverride=*/ false);
// Check that "schema1" is still platform hidden, but "schema2" is the default platform
// visible.
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .getSchemasNotPlatformSurfaceable("database"))
.containsExactly("database/schema1");
}
@Test
public void testRemoveSchema() throws Exception {
- Set<AppSearchSchema> schemas = new HashSet<>();
- schemas.add(new AppSearchSchema.Builder("Email").build());
- schemas.add(new AppSearchSchema.Builder("Document").build());
+ List<AppSearchSchema> schemas =
+ ImmutableList.of(
+ new AppSearchSchema.Builder("Email").build(),
+ new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1
- mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
+ mAppSearchImpl.setSchema(
+ "database1",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto =
@@ -547,20 +579,27 @@ public class AppSearchImplTest {
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
.containsExactlyElementsIn(expectedTypes);
- final Set<AppSearchSchema> finalSchemas =
- Collections.singleton(new AppSearchSchema.Builder("Email").build());
+ final List<AppSearchSchema> finalSchemas =
+ Collections.singletonList(new AppSearchSchema.Builder("Email").build());
// Check the incompatible error has been thrown.
AppSearchException e =
expectThrows(
AppSearchException.class,
() ->
mAppSearchImpl.setSchema(
- "database1", finalSchemas, /*forceOverride=*/ false));
+ "database1",
+ finalSchemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false));
assertThat(e).hasMessageThat().contains("Schema is incompatible");
assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
// ForceOverride to delete.
- mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/ true);
+ mAppSearchImpl.setSchema(
+ "database1",
+ finalSchemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ true);
// Check Document schema is removed.
expectedProto =
@@ -579,13 +618,22 @@ public class AppSearchImplTest {
@Test
public void testRemoveSchema_differentDataBase() throws Exception {
// Create schemas
- Set<AppSearchSchema> schemas = new HashSet<>();
- schemas.add(new AppSearchSchema.Builder("Email").build());
- schemas.add(new AppSearchSchema.Builder("Document").build());
+ List<AppSearchSchema> schemas =
+ ImmutableList.of(
+ new AppSearchSchema.Builder("Email").build(),
+ new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1 and 2
- mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false);
- mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false);
+ mAppSearchImpl.setSchema(
+ "database1",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
+ mAppSearchImpl.setSchema(
+ "database2",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto =
@@ -610,8 +658,12 @@ public class AppSearchImplTest {
.containsExactlyElementsIn(expectedTypes);
// Save only Email to database1 this time.
- schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build());
- mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ true);
+ schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build());
+ mAppSearchImpl.setSchema(
+ "database1",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ true);
// Create expected schemaType list, database 1 should only contain Email but database 2
// remains in same.
@@ -638,76 +690,82 @@ public class AppSearchImplTest {
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
mAppSearchImpl.setSchema(
"database",
- Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
/*forceOverride=*/ false);
- mAppSearchImpl.setVisibility("database", Set.of("schema1"));
// "schema1" is platform hidden now
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .getSchemasNotPlatformSurfaceable("database"))
.containsExactly("database/schema1");
// Remove "schema1" by force overriding
- mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/ true);
+ mAppSearchImpl.setSchema(
+ "database",
+ Collections.emptyList(),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*forceOverride=*/ true);
// Check that "schema1" is no longer considered platform hidden
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .getSchemasNotPlatformSurfaceable("database"))
.isEmpty();
// Add "schema1" back, it gets default visibility settings which means it's not platform
// hidden.
mAppSearchImpl.setSchema(
"database",
- Collections.singleton(new AppSearchSchema.Builder("schema1").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .getSchemasNotPlatformSurfaceable("database"))
.isEmpty();
}
@Test
- public void testSetVisibility_defaultPlatformVisible() throws Exception {
+ public void testSetSchema_defaultPlatformVisible() throws Exception {
mAppSearchImpl.setSchema(
"database",
- Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .getSchemasNotPlatformSurfaceable("database"))
.isEmpty();
}
@Test
- public void testSetVisibility_platformHidden() throws Exception {
+ public void testSetSchema_platformHidden() throws Exception {
mAppSearchImpl.setSchema(
"database",
- Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
/*forceOverride=*/ false);
- mAppSearchImpl.setVisibility("database", Set.of("Schema"));
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database"))
+ assertThat(
+ mAppSearchImpl
+ .getVisibilityStoreLocked()
+ .getSchemasNotPlatformSurfaceable("database"))
.containsExactly("database/Schema");
}
@Test
- public void testSetVisibility_unknownSchema() throws Exception {
- mAppSearchImpl.setSchema(
- "database",
- Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
- /*forceOverride=*/ false);
-
- // We'll throw an exception if a client tries to set visibility on a schema we don't know
- // about.
- AppSearchException e =
- expectThrows(
- AppSearchException.class,
- () -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema")));
- assertThat(e).hasMessageThat().contains("Unknown schema(s)");
- }
-
- @Test
public void testHasSchemaType() throws Exception {
// Nothing exists yet
assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isFalse();
mAppSearchImpl.setSchema(
"database",
- Collections.singleton(new AppSearchSchema.Builder("Schema").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue();
@@ -723,7 +781,8 @@ public class AppSearchImplTest {
// Has database1
mAppSearchImpl.setSchema(
"database1",
- Collections.singleton(new AppSearchSchema.Builder("schema").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getDatabasesLocked())
.containsExactly(VisibilityStore.DATABASE_NAME, "database1");
@@ -731,7 +790,8 @@ public class AppSearchImplTest {
// Has both databases
mAppSearchImpl.setSchema(
"database2",
- Collections.singleton(new AppSearchSchema.Builder("schema").build()),
+ Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
assertThat(mAppSearchImpl.getDatabasesLocked())
.containsExactly(VisibilityStore.DATABASE_NAME, "database1", "database2");
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
index dfe2de6538a4..a1f575a8720e 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java
@@ -41,34 +41,19 @@ public class VisibilityStoreTest {
@Test
public void testSetVisibility() throws Exception {
mVisibilityStore.setVisibility(
- "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
- assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+ "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema2"));
+ assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database"))
.containsExactly("schema1", "schema2");
// New .setVisibility() call completely overrides previous visibility settings. So
// "schema1" isn't preserved.
mVisibilityStore.setVisibility(
- "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema3"));
- assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
+ "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema3"));
+ assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database"))
.containsExactly("schema1", "schema3");
mVisibilityStore.setVisibility(
- "database", /*platformHiddenSchemas=*/ Collections.emptySet());
- assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
- }
-
- @Test
- public void testRemoveSchemas() throws Exception {
- mVisibilityStore.setVisibility(
- "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2"));
-
- // Removed just schema1
- mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema1"));
- assertThat(mVisibilityStore.getPlatformHiddenSchemas("database"))
- .containsExactly("schema2");
-
- // Removed everything now
- mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2"));
- assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty();
+ "database", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet());
+ assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")).isEmpty();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 98392a71be58..194be3761903 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -94,20 +94,24 @@ public class GenericDocumentToProtoConverterTest {
PropertyProto.newBuilder()
.setName("documentKey1")
.addDocumentValues(
- GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1)));
+ GenericDocumentToProtoConverter.toDocumentProto(
+ DOCUMENT_PROPERTIES_1)));
propertyProtoMap.put(
"documentKey2",
PropertyProto.newBuilder()
.setName("documentKey2")
.addDocumentValues(
- GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2)));
+ GenericDocumentToProtoConverter.toDocumentProto(
+ DOCUMENT_PROPERTIES_2)));
List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet());
Collections.sort(sortedKey);
for (String key : sortedKey) {
documentProtoBuilder.addProperties(propertyProtoMap.get(key));
}
DocumentProto documentProto = documentProtoBuilder.build();
- assertThat(GenericDocumentToProtoConverter.convert(document)).isEqualTo(documentProto);
- assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto));
+ assertThat(GenericDocumentToProtoConverter.toDocumentProto(document))
+ .isEqualTo(documentProto);
+ assertThat(document)
+ .isEqualTo(GenericDocumentToProtoConverter.toGenericDocument(documentProto));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
index dedfca42ff90..88edcb857aaf 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
@@ -89,7 +89,10 @@ public class SchemaToProtoConverterTest {
TermMatchType.Code.PREFIX)))
.build();
- assertThat(SchemaToProtoConverter.convert(emailSchema)).isEqualTo(expectedEmailProto);
+ assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(emailSchema))
+ .isEqualTo(expectedEmailProto);
+ assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedEmailProto))
+ .isEqualTo(emailSchema);
}
@Test
@@ -151,7 +154,9 @@ public class SchemaToProtoConverterTest {
TermMatchType.Code.UNKNOWN)))
.build();
- assertThat(SchemaToProtoConverter.convert(musicRecordingSchema))
+ assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(musicRecordingSchema))
.isEqualTo(expectedMusicRecordingProto);
+ assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedMusicRecordingProto))
+ .isEqualTo(musicRecordingSchema);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 518f53205588..7c68c6be4883 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -83,7 +83,7 @@ public class SnippetTest {
// Making ResultReader and getting Snippet values.
SearchResultPage searchResultPage =
- SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto);
+ SearchResultToProtoConverter.toSearchResultPage(searchResultProto);
for (SearchResult result : searchResultPage.getResults()) {
SearchResult.MatchInfo match = result.getMatches().get(0);
assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
@@ -131,7 +131,7 @@ public class SnippetTest {
SearchResultProto.newBuilder().addResults(resultProto).build();
SearchResultPage searchResultPage =
- SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto);
+ SearchResultToProtoConverter.toSearchResultPage(searchResultProto);
for (SearchResult result : searchResultPage.getResults()) {
assertThat(result.getMatches()).isEmpty();
}
@@ -196,7 +196,7 @@ public class SnippetTest {
// Making ResultReader and getting Snippet values.
SearchResultPage searchResultPage =
- SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto);
+ SearchResultToProtoConverter.toSearchResultPage(searchResultProto);
for (SearchResult result : searchResultPage.getResults()) {
SearchResult.MatchInfo match1 = result.getMatches().get(0);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 444155d12b3f..738527e1df0b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -18,14 +18,17 @@ package com.android.server.backup.utils;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.backup.BackupManager.OperationType;
+import android.compat.testing.PlatformCompatChangeRule;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
@@ -38,9 +41,15 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.UserBackupManagerService;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -50,17 +59,19 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class BackupEligibilityRulesTest {
private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent";
- private static final String TEST_PACKAGE_NAME = "test_package";
+ private static final String TEST_PACKAGE_NAME = "com.android.frameworks.servicestests";
private static final Signature SIGNATURE_1 = generateSignature((byte) 1);
private static final Signature SIGNATURE_2 = generateSignature((byte) 2);
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
+ @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
@Mock private PackageManagerInternal mMockPackageManagerInternal;
@Mock private PackageManager mPackageManager;
- private BackupEligibilityRules mBackupEligibilityRules;
+ private BackupEligibilityRules mBackupEligibilityRules;
private int mUserId;
@Before
@@ -225,7 +236,6 @@ public class BackupEligibilityRulesTest {
throws Exception {
ApplicationInfo applicationInfo = getApplicationInfo(Process.SYSTEM_UID,
/* flags */ 0, CUSTOM_BACKUP_AGENT_NAME);
-
BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
OperationType.MIGRATION);
boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
@@ -234,6 +244,82 @@ public class BackupEligibilityRulesTest {
}
@Test
+ @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP})
+ public void appIsEligibleForBackup_adbBackupNotAllowed_returnsFalseForAdbBackup()
+ throws Exception {
+ ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
+ /* flags */ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, CUSTOM_BACKUP_AGENT_NAME);
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.ADB_BACKUP);
+ when(mPackageManager.getProperty(eq(PackageManager.PROPERTY_ALLOW_ADB_BACKUP),
+ eq(TEST_PACKAGE_NAME))).thenReturn(getAdbBackupProperty(
+ /* allowAdbBackup */ false));
+
+ boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
+ @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP})
+ public void appIsEligibleForBackup_adbBackupAllowed_returnsTrueForAdbBackup()
+ throws Exception {
+ ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
+ /* flags */ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, CUSTOM_BACKUP_AGENT_NAME);
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.ADB_BACKUP);
+ when(mPackageManager.getProperty(eq(PackageManager.PROPERTY_ALLOW_ADB_BACKUP),
+ eq(TEST_PACKAGE_NAME))).thenReturn(getAdbBackupProperty(
+ /* allowAdbBackup */ true));
+
+ boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ @EnableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP})
+ public void appIsEligibleForBackup_debuggableNonPrivilegedApp_returnsTrueForAdbBackup()
+ throws Exception {
+ ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
+ /* flags */ ApplicationInfo.FLAG_DEBUGGABLE, CUSTOM_BACKUP_AGENT_NAME);
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.ADB_BACKUP);
+
+ boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ @DisableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP})
+ public void appIsEligibleForBackup_allowBackupTrueBeforeS_returnsTrueForAdbBackup()
+ throws Exception {
+ ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
+ ApplicationInfo.FLAG_ALLOW_BACKUP, CUSTOM_BACKUP_AGENT_NAME);
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.ADB_BACKUP);
+
+ boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ @DisableCompatChanges({BackupEligibilityRules.RESTRICT_ADB_BACKUP})
+ public void appIsEligibleForBackup_allowBackupFalseBeforeS_returnsFalseForAdbBackup()
+ throws Exception {
+ ApplicationInfo applicationInfo = getApplicationInfo(Process.FIRST_APPLICATION_UID,
+ /* flags */ 0, CUSTOM_BACKUP_AGENT_NAME);
+ BackupEligibilityRules eligibilityRules = getBackupEligibilityRules(
+ OperationType.ADB_BACKUP);
+
+ boolean isEligible = eligibilityRules.appIsEligibleForBackup(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
public void appIsDisabled_stateDefaultManifestEnabled_returnsFalse() throws Exception {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.flags = 0;
@@ -789,10 +875,15 @@ public class BackupEligibilityRulesTest {
private static ApplicationInfo getApplicationInfo(int appUid, int flags,
String backupAgentName) {
ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.flags = 0;
+ applicationInfo.flags = flags;
applicationInfo.packageName = TEST_PACKAGE_NAME;
applicationInfo.uid = appUid;
applicationInfo.backupAgentName = backupAgentName;
return applicationInfo;
}
+
+ private static Property getAdbBackupProperty(boolean allowAdbBackup) {
+ return new Property(PackageManager.PROPERTY_ALLOW_ADB_BACKUP, allowAdbBackup,
+ TEST_PACKAGE_NAME, /* className */ "");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index cb5ca04c1fde..603608b23172 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -40,6 +40,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
@@ -402,7 +403,7 @@ public class DisplayModeDirectorTest {
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
- .isNotEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE);
+ .isNotEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
@@ -411,9 +412,9 @@ public class DisplayModeDirectorTest {
assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(30);
- director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_NONE);
+ director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_NONE);
assertThat(director.getModeSwitchingType())
- .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_NONE);
+ .isEqualTo(DisplayManager.SWITCHING_TYPE_NONE);
desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(30);
@@ -428,9 +429,9 @@ public class DisplayModeDirectorTest {
final int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
- director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS);
+ director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
assertThat(director.getModeSwitchingType())
- .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_WITHIN_GROUPS);
+ .isEqualTo(DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
assertThat(desiredSpecs.allowGroupSwitching).isFalse();
}
@@ -440,9 +441,9 @@ public class DisplayModeDirectorTest {
final int displayId = 0;
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
- director.setModeSwitchingType(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+ director.setModeSwitchingType(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
assertThat(director.getModeSwitchingType())
- .isEqualTo(DisplayModeDirector.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+ .isEqualTo(DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
assertThat(desiredSpecs.allowGroupSwitching).isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 6debc893ea1b..f2254a98a70e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -98,17 +98,17 @@ public class ActiveSourceActionTest {
}
@Override
- PowerManager getPowerManager() {
+ protected PowerManager getPowerManager() {
return powerManager;
}
@Override
- void writeStringSystemProperty(String key, String value) {
+ protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index a19336eeb5ea..6e4d994bd416 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -90,7 +90,7 @@ public class ArcInitiationActionFromAvrTest {
}
@Override
- PowerManager getPowerManager() {
+ protected PowerManager getPowerManager() {
return powerManager;
}
@@ -110,7 +110,7 @@ public class ArcInitiationActionFromAvrTest {
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index cd6977524943..bbe1156c5d61 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -86,7 +86,7 @@ public class ArcTerminationActionFromAvrTest {
}
@Override
- PowerManager getPowerManager() {
+ protected PowerManager getPowerManager() {
return powerManager;
}
@@ -111,7 +111,7 @@ public class ArcTerminationActionFromAvrTest {
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
new file mode 100644
index 000000000000..af119c8c4267
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.Constants.PATH_RELATIONSHIP_ANCESTOR;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.stats.hdmi.nano.HdmiStatsEnums;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the {@link HdmiCecAtomWriter} class and its usage by the HDMI-CEC framework.
+ */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class HdmiCecAtomLoggingTest {
+ private HdmiCecAtomWriter mHdmiCecAtomWriterSpy;
+ private HdmiControlService mHdmiControlServiceSpy;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
+ private HdmiMhlControllerStub mHdmiMhlControllerStub;
+ private FakeNativeWrapper mNativeWrapper;
+ private HdmiCecNetwork mHdmiCecNetwork;
+ private Looper mLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private int mPhysicalAddress = 0x1110;
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private HdmiPortInfo[] mHdmiPortInfo;
+
+ @Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ mHdmiCecAtomWriterSpy = spy(new HdmiCecAtomWriter());
+
+ mLooper = mTestLooper.getLooper();
+
+ Context mContextSpy = spy(new ContextWrapper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
+
+ PowerManager powerManager = new PowerManager(
+ mContextSpy, mIPowerManagerMock, mIThermalServiceMock, new Handler(mLooper));
+ doReturn(powerManager).when(mContextSpy).getSystemService(Context.POWER_SERVICE);
+ doReturn(true).when(mIPowerManagerMock).isInteractive();
+
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+ doNothing().when(mHdmiControlServiceSpy)
+ .writeStringSystemProperty(anyString(), anyString());
+ doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
+
+ HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
+ doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig();
+
+ mHdmiControlServiceSpy.setIoLooper(mLooper);
+ mHdmiControlServiceSpy.setMessageValidator(
+ new HdmiCecMessageValidator(mHdmiControlServiceSpy));
+ mHdmiControlServiceSpy.setCecMessageBuffer(
+ new CecMessageBuffer(mHdmiControlServiceSpy));
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
+
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlServiceSpy, mNativeWrapper, mHdmiCecAtomWriterSpy);
+ mHdmiControlServiceSpy.setCecController(mHdmiCecController);
+
+ mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlServiceSpy);
+ mHdmiControlServiceSpy.setHdmiMhlController(
+ mHdmiMhlControllerStub);
+
+ mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlServiceSpy,
+ mHdmiCecController, mHdmiMhlControllerStub);
+ mHdmiControlServiceSpy.setHdmiCecNetwork(mHdmiCecNetwork);
+
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mNativeWrapper.setPortConnectionStatus(1, true);
+
+ mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlServiceSpy);
+ mHdmiCecLocalDevicePlayback.init();
+ mLocalDevices.add(mHdmiCecLocalDevicePlayback);
+
+ mHdmiControlServiceSpy.initService();
+ mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testActiveSourceChanged_calledOnSetActiveSource() {
+ mHdmiControlServiceSpy.setActiveSource(1, 0x1111, "caller");
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .activeSourceChanged(1, 0x1111, PATH_RELATIONSHIP_ANCESTOR);
+ }
+
+ @Test
+ public void testMessageReported_calledOnOutgoingMessage() {
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+ mPhysicalAddress);
+
+ mHdmiCecController.sendCommand(message);
+
+ verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
+ message,
+ HdmiStatsEnums.OUTGOING,
+ SendMessageResult.SUCCESS);
+ }
+
+ @Test
+ public void testMessageReported_calledOnIncomingMessage() {
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+
+ mNativeWrapper.onCecMessage(message);
+ mTestLooper.dispatchAll();
+
+ verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
+ message,
+ HdmiStatsEnums.INCOMING);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index d10e075c5136..777713e4ed5a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -99,7 +99,7 @@ public final class HdmiCecConfigTest {
+ "</cec-settings>", null);
assertThat(hdmiCecConfig.getAllSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
}
@Test
@@ -147,7 +147,7 @@ public final class HdmiCecConfigTest {
+ "</cec-settings>", null);
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
}
@Test
@@ -230,7 +230,7 @@ public final class HdmiCecConfigTest {
+ " </setting>"
+ "</cec-settings>", null);
assertTrue(hdmiCecConfig.isStringValueType(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
}
@Test
@@ -330,10 +330,10 @@ public final class HdmiCecConfigTest {
+ " </setting>"
+ "</cec-settings>", null);
assertThat(hdmiCecConfig.getAllowedStringValues(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
- .containsExactly(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+ .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
+ HdmiControlManager.POWER_CONTROL_MODE_NONE);
}
@Test
@@ -374,7 +374,7 @@ public final class HdmiCecConfigTest {
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getAllowedIntValues(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
}
@Test
@@ -479,8 +479,8 @@ public final class HdmiCecConfigTest {
+ " </setting>"
+ "</cec-settings>", null);
assertThat(hdmiCecConfig.getDefaultStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
- .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+ .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
}
@Test
@@ -521,7 +521,7 @@ public final class HdmiCecConfigTest {
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getDefaultIntValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
}
@Test
@@ -610,8 +610,8 @@ public final class HdmiCecConfigTest {
public void getStringValue_GlobalSetting_BasicSanity() {
when(mStorageAdapter.retrieveGlobalSetting(
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV))
- .thenReturn(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.POWER_CONTROL_MODE_TV))
+ .thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
mContext, mStorageAdapter,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
@@ -628,8 +628,8 @@ public final class HdmiCecConfigTest {
+ " </setting>"
+ "</cec-settings>", null);
assertThat(hdmiCecConfig.getStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP))
- .isEqualTo(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+ .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
@Test
@@ -696,7 +696,7 @@ public final class HdmiCecConfigTest {
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP));
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
}
@Test
@@ -812,8 +812,8 @@ public final class HdmiCecConfigTest {
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST));
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST));
}
@Test
@@ -835,7 +835,7 @@ public final class HdmiCecConfigTest {
+ "</cec-settings>", null);
assertThrows(IllegalArgumentException.class,
() -> hdmiCecConfig.setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
"bar"));
}
@@ -856,11 +856,11 @@ public final class HdmiCecConfigTest {
+ " <default-value string-value=\"to_tv\" />"
+ " </setting>"
+ "</cec-settings>", null);
- hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
verify(mStorageAdapter).storeGlobalSetting(
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 3fd3ce3cd0df..6bb68da2a894 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -170,7 +170,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Override
- void writeStringSystemProperty(String key, String value) {
+ protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -185,12 +185,12 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Override
- PowerManager getPowerManager() {
+ protected PowerManager getPowerManager() {
return powerManager;
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index dfeed1362b81..f7d52b6e8b8c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -18,7 +18,6 @@ package com.android.server.hdmi;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_INVALID;
-import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -114,7 +113,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Override
- void writeStringSystemProperty(String key, String value) {
+ protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -124,12 +123,12 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Override
- PowerManager getPowerManager() {
+ protected PowerManager getPowerManager() {
return powerManager;
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
@@ -557,8 +556,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleOnStandby_ScreenOff_NotActiveSource_ToTv() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
@@ -577,8 +576,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleOnStandby_ScreenOff_NotActiveSource_Broadcast() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
@@ -597,8 +596,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleOnStandby_ScreenOff_NotActiveSource_None() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
@@ -617,8 +616,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleOnStandby_ScreenOff_ActiveSource_ToTv() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
@@ -637,8 +636,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleOnStandby_ScreenOff_ActiveSource_Broadcast() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
@@ -657,8 +656,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleOnStandby_ScreenOff_ActiveSource_None() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
mHdmiCecLocalDevicePlayback.setAutoDeviceOff(true);
@@ -828,12 +827,12 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mStandby = false;
// 1. DUT is <AS>.
- HdmiCecMessage message1 = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
- mPlaybackPhysicalAddress);
+ HdmiCecMessage message1 = HdmiCecMessageBuilder.buildActiveSource(
+ mHdmiCecLocalDevicePlayback.mAddress, mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message1)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
assertThat(mStandby).isFalse();
@@ -1141,8 +1140,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void oneTouchPlay_SendStandbyOnSleepToTv() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
@@ -1164,8 +1163,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void oneTouchPlay_SendStandbyOnSleepBroadcast() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
@@ -1187,8 +1186,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void oneTouchPlay_SendStandbyOnSleepNone() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_NONE);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
@@ -1266,8 +1265,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void toggleAndFollowTvPower_ToTv_TvStatusOn() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV);
mStandby = false;
mHdmiControlService.toggleAndFollowTvPower();
HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
@@ -1284,8 +1283,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void toggleAndFollowTvPower_Broadcast_TvStatusOn() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
- HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mStandby = false;
mHdmiControlService.toggleAndFollowTvPower();
HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 12414d99d991..d24b376793cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -88,17 +88,17 @@ public class HdmiCecLocalDeviceTvTest {
}
@Override
- void writeStringSystemProperty(String key, String value) {
+ protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@Override
- PowerManager getPowerManager() {
+ protected PowerManager getPowerManager() {
return powerManager;
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index a05cbb48a3f7..6285f58d07b3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -56,6 +56,30 @@ public class HdmiCecMessageValidatorTest {
}
@Test
+ public void isValid_unregisteredSource() {
+ // Message invokes a broadcast response
+ // <Get Menu Language>
+ assertMessageValidity("F4:91").isEqualTo(OK);
+ // <Request Active Source>
+ assertMessageValidity("FF:85").isEqualTo(OK);
+
+ // Message by CEC Switch
+ // <Routing Change>
+ assertMessageValidity("FF:80:00:00:10:00").isEqualTo(OK);
+
+ // <Routing Information>
+ assertMessageValidity("FF:81:10:00").isEqualTo(OK);
+
+ // Standby
+ assertMessageValidity("F4:36").isEqualTo(OK);
+ assertMessageValidity("FF:36").isEqualTo(OK);
+
+ // <Report Physical Address> / <Active Source>
+ assertMessageValidity("FF:84:10:00:04").isEqualTo(OK);
+ assertMessageValidity("FF:82:10:00").isEqualTo(OK);
+ }
+
+ @Test
public void isValid_giveDevicePowerStatus() {
assertMessageValidity("04:8F").isEqualTo(OK);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 3cc7c6b88a0d..670d51207e31 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -95,7 +95,7 @@ public class HdmiCecPowerStatusControllerTest {
}
@Override
- void writeStringSystemProperty(String key, String value) {
+ protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -110,7 +110,7 @@ public class HdmiCecPowerStatusControllerTest {
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 819bd01992cb..25138073ca40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -151,7 +151,7 @@ public class HdmiControlServiceTest {
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index 0a225a06b380..f80b5737d27b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -162,7 +162,7 @@ public class SystemAudioInitiationActionFromAvrTest {
}
@Override
- HdmiCecConfig getHdmiCecConfig() {
+ protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
};
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index f823bb957a33..2471210f6325 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -22,6 +22,8 @@ import static android.service.notification.NotificationListenerService.NOTIFICAT
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -58,6 +60,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutChangeCallback;
+import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
@@ -136,6 +139,7 @@ public final class DataManagerTest {
@Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor;
@Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+ @Captor private ArgumentCaptor<Integer> mQueryFlagsCaptor;
private ScheduledExecutorService mExecutorService;
private NotificationChannel mNotificationChannel;
@@ -854,6 +858,8 @@ public final class DataManagerTest {
List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
assertEquals(1, result.size());
assertEquals(shortcut.getId(), result.get(0).getShortcutInfo().getId());
+ assertEquals(1, result.get(0).getShortcutInfo().getPersons().length);
+ assertEquals(CONTACT_URI, result.get(0).getShortcutInfo().getPersons()[0].getUri());
assertEquals(mParentNotificationChannel.getId(),
result.get(0).getParentNotificationChannel().getId());
assertEquals(mStatusBarNotification.getPostTime(), result.get(0).getLastEventTimestamp());
@@ -861,6 +867,28 @@ public final class DataManagerTest {
}
@Test
+ public void testGetRecentConversationsGetsPersonsData() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
+
+ verify(mShortcutServiceInternal).getShortcuts(
+ anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
+ mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt());
+ Integer queryFlags = mQueryFlagsCaptor.getValue();
+ assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue();
+ }
+
+ @Test
public void testPruneOldRecentConversations() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1068,6 +1096,13 @@ public final class DataManagerTest {
return new UserInfo(userId, "", 0);
}
+ /**
+ * Returns {@code true} iff {@link ShortcutQuery}'s {@code queryFlags} has {@code flag} set.
+ */
+ private static boolean hasFlag(int queryFlags, int flag) {
+ return (queryFlags & flag) != 0;
+ }
+
private class TestContactsQueryHelper extends ContactsQueryHelper {
private Uri mContactUri;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index f8e92ad71d8e..84551c51052c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -22,6 +22,7 @@ import android.util.ArraySet;
import android.util.SparseArray;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
import java.io.File;
import java.util.Map;
@@ -38,14 +39,14 @@ public class PackageSettingBuilder {
private int mPkgFlags;
private int mPrivateFlags;
private int mSharedUserId;
- private String[] mUsesStaticLibraries;
- private long[] mUsesStaticLibrariesVersions;
- private Map<String, ArraySet<String>> mMimeGroups;
private String mVolumeUuid;
+ private int mAppId;
private SparseArray<PackageUserState> mUserStates = new SparseArray<>();
private AndroidPackage mPkg;
- private int mAppId;
private InstallSource mInstallSource;
+ private String[] mUsesStaticLibraries;
+ private long[] mUsesStaticLibrariesVersions;
+ private Map<String, ArraySet<String>> mMimeGroups;
private PackageParser.SigningDetails mSigningDetails;
public PackageSettingBuilder setPackage(AndroidPackage pkg) {
@@ -143,6 +144,14 @@ public class PackageSettingBuilder {
return this;
}
+ public PackageSettingBuilder setInstallState(int userId, boolean installed) {
+ if (mUserStates.indexOfKey(userId) < 0) {
+ mUserStates.put(userId, new PackageUserState());
+ }
+ mUserStates.get(userId).installed = installed;
+ return this;
+ }
+
public PackageSettingBuilder setInstallSource(InstallSource installSource) {
mInstallSource = installSource;
return this;
@@ -173,6 +182,5 @@ public class PackageSettingBuilder {
packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i));
}
return packageSetting;
-
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index fcbb5ed1140c..d8c3979c9cf9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -101,7 +101,8 @@ public class ScanTests {
@Before
public void setupDefaultAbiBehavior() throws Exception {
when(mMockPackageAbiHelper.derivePackageAbi(
- any(AndroidPackage.class), anyBoolean(), nullable(String.class)))
+ any(AndroidPackage.class), anyBoolean(), nullable(String.class),
+ any(File.class)))
.thenReturn(new Pair<>(
new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"),
new PackageAbiHelper.NativeLibraryPaths(
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 3ce7a7d73d4a..eaf62cbb8201 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -227,8 +227,8 @@ public class DexMetadataHelperTest {
File dm = createDexMetadataFile("install_split_base.apk");
try (FileInputStream is = new FileInputStream(base)) {
ApkLite baseApk = PackageParser.parseApkLite(is.getFD(), base.getAbsolutePath(), 0);
- PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
- null, null);
+ PackageLite pkgLite = new PackageLite(null, baseApk.codePath, baseApk, null, null, null,
+ null, null, null);
Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 92942bb91528..e816cbebd73f 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -23,6 +23,7 @@ import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_S
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -170,16 +171,16 @@ public final class DeviceStateProviderImplTest {
@Test
public void create_sensor() throws Exception {
- Sensor sensor = newSensor("sensor", Sensor.TYPE_HINGE_ANGLE);
- when(mSensorManager.getSensorList(eq(sensor.getType()))).thenReturn(List.of(sensor));
+ Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
String configString = "<device-state-config>\n"
+ " <device-state>\n"
+ " <identifier>1</identifier>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ + " <type>" + sensor.getStringType() + "</type>\n"
+ " <name>" + sensor.getName() + "</name>\n"
- + " <type>" + sensor.getType() + "</type>\n"
+ " <value>\n"
+ " <max>90</max>\n"
+ " </value>\n"
@@ -190,8 +191,8 @@ public final class DeviceStateProviderImplTest {
+ " <identifier>2</identifier>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ + " <type>" + sensor.getStringType() + "</type>\n"
+ " <name>" + sensor.getName() + "</name>\n"
- + " <type>" + sensor.getType() + "</type>\n"
+ " <value>\n"
+ " <min-inclusive>90</min-inclusive>\n"
+ " <max>180</max>\n"
@@ -203,8 +204,8 @@ public final class DeviceStateProviderImplTest {
+ " <identifier>3</identifier>\n"
+ " <conditions>\n"
+ " <sensor>\n"
+ + " <type>" + sensor.getStringType() + "</type>\n"
+ " <name>" + sensor.getName() + "</name>\n"
- + " <type>" + sensor.getType() + "</type>\n"
+ " <value>\n"
+ " <min-inclusive>180</min-inclusive>\n"
+ " </value>\n"
@@ -262,13 +263,13 @@ public final class DeviceStateProviderImplTest {
assertEquals(1, mIntegerCaptor.getValue().intValue());
}
- private static Sensor newSensor(String name, int type) throws Exception {
+ private static Sensor newSensor(String name, String type) throws Exception {
Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor();
constructor.setAccessible(true);
Sensor sensor = constructor.newInstance();
FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mName"), name);
- FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mType"), type);
+ FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mStringType"), type);
return sensor;
}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index fb6b29e4188e..9bd488d3df7e 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerSaveState;
+import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -47,19 +48,19 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
private static final int GPS_MODE = 0; // LOCATION_MODE_NO_CHANGE
private static final int DEFAULT_GPS_MODE =
PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
- private static final String BATTERY_SAVER_CONSTANTS = "vibration_disabled=true,"
+ private static final String BATTERY_SAVER_CONSTANTS = "disable_vibration=true,"
+ "advertise_is_enabled=true,"
- + "animation_disabled=false,"
- + "soundtrigger_disabled=true,"
- + "firewall_disabled=false,"
- + "datasaver_disabled=false,"
- + "adjust_brightness_disabled=true,"
+ + "disable_animation=false,"
+ + "disable_soundtrigger=true,"
+ + "enable_firewall=true,"
+ + "enable_datasaver=true,"
+ + "enable_brightness_adjustment=false,"
+ "adjust_brightness_factor=0.7,"
- + "fullbackup_deferred=true,"
- + "keyvaluebackup_deferred=false,"
- + "gps_mode=0," // LOCATION_MODE_NO_CHANGE
+ + "defer_full_backup=true,"
+ + "defer_keyvalue_backup=false,"
+ + "location_mode=0," // LOCATION_MODE_NO_CHANGE
+ "enable_night_mode=false,"
- + "quick_doze_enabled=true";
+ + "enable_quick_doze=true";
private static final String BATTERY_SAVER_INCORRECT_CONSTANTS = "vi*,!=,,true";
private class BatterySaverPolicyForTest extends BatterySaverPolicy {
@@ -336,13 +337,15 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
}
mBatterySaverPolicy.setAdaptivePolicyLocked(
- Policy.fromSettings(BATTERY_SAVER_CONSTANTS, ""));
+ Policy.fromSettings(BATTERY_SAVER_CONSTANTS, "",
+ new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_BATTERY_SAVER).build(), null));
verifyBatterySaverConstantsUpdated();
}
public void testAutomotiveProjectionChanges_Full() {
mBatterySaverPolicy.updateConstantsLocked(
- "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+ "location_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+ ",enable_night_mode=true", "");
mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
@@ -369,8 +372,10 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
public void testAutomotiveProjectionChanges_Adaptive() {
mBatterySaverPolicy.setAdaptivePolicyLocked(
Policy.fromSettings(
- "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
- + ",enable_night_mode=true", ""));
+ "location_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+ + ",enable_night_mode=true", "",
+ new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_BATTERY_SAVER).build(), null));
mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE);
assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
.isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
@@ -392,4 +397,188 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
ServiceType.NIGHT_MODE).batterySaverEnabled);
}
+
+ public void testUserSettingsOverrideDeviceConfig() {
+ Policy policy = Policy.fromSettings(
+ BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR + "=.1"
+ + "," + BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED + "=true"
+ + "," + BatterySaverPolicy.KEY_DEFER_FULL_BACKUP + "=true"
+ + "," + BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP + "=true"
+ + "," + BatterySaverPolicy.KEY_DISABLE_ANIMATION + "=true"
+ + "," + BatterySaverPolicy.KEY_DISABLE_AOD + "=true"
+ + "," + BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST + "=true"
+ + "," + BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS + "=true"
+ + "," + BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER + "=true"
+ + "," + BatterySaverPolicy.KEY_DISABLE_VIBRATION + "=true"
+ + "," + BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT + "=true"
+ + "," + BatterySaverPolicy.KEY_ENABLE_DATASAVER + "=true"
+ + "," + BatterySaverPolicy.KEY_ENABLE_FIREWALL + "=true"
+ + "," + BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE + "=true"
+ + "," + BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE + "=true"
+ + "," + BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY + "=true"
+ + "," + BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK + "=true"
+ + "," + BatterySaverPolicy.KEY_LOCATION_MODE
+ + "=" + PowerManager.LOCATION_MODE_FOREGROUND_ONLY,
+ "",
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_BATTERY_SAVER)
+ .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR, .5f)
+ .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED, false)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP, false)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE, false)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY, false)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK, false)
+ .setInt(BatterySaverPolicy.KEY_LOCATION_MODE,
+ PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF)
+ .build(),
+ null);
+ assertEquals(.1f, policy.adjustBrightnessFactor);
+ assertTrue(policy.advertiseIsEnabled);
+ assertTrue(policy.deferFullBackup);
+ assertTrue(policy.deferKeyValueBackup);
+ assertTrue(policy.disableAnimation);
+ assertTrue(policy.disableAod);
+ assertTrue(policy.disableLaunchBoost);
+ assertTrue(policy.disableOptionalSensors);
+ assertTrue(policy.disableSoundTrigger);
+ assertTrue(policy.disableVibration);
+ assertTrue(policy.enableAdjustBrightness);
+ assertTrue(policy.enableDataSaver);
+ assertTrue(policy.enableFirewall);
+ assertTrue(policy.enableNightMode);
+ assertTrue(policy.enableQuickDoze);
+ assertTrue(policy.forceAllAppsStandby);
+ assertTrue(policy.forceBackgroundCheck);
+ assertEquals(PowerManager.LOCATION_MODE_FOREGROUND_ONLY, policy.locationMode);
+ }
+
+ public void testDeviceConfigOverridesDefaults() {
+ Policy policy = Policy.fromSettings(
+ "", "",
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_BATTERY_SAVER)
+ .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR, .5f)
+ .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED, false)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP, false)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE, false)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY, false)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK, false)
+ .setInt(BatterySaverPolicy.KEY_LOCATION_MODE,
+ PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF)
+ .build(),
+ null);
+ assertEquals(.5f, policy.adjustBrightnessFactor);
+ assertFalse(policy.advertiseIsEnabled);
+ assertFalse(policy.deferFullBackup);
+ assertFalse(policy.deferKeyValueBackup);
+ assertFalse(policy.disableAnimation);
+ assertFalse(policy.disableAod);
+ assertFalse(policy.disableLaunchBoost);
+ assertFalse(policy.disableOptionalSensors);
+ assertFalse(policy.disableSoundTrigger);
+ assertFalse(policy.disableVibration);
+ assertFalse(policy.enableAdjustBrightness);
+ assertFalse(policy.enableDataSaver);
+ assertFalse(policy.enableFirewall);
+ assertFalse(policy.enableNightMode);
+ assertFalse(policy.enableQuickDoze);
+ assertFalse(policy.forceAllAppsStandby);
+ assertFalse(policy.forceBackgroundCheck);
+ assertEquals(PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF,
+ policy.locationMode);
+ }
+
+ public void testDeviceConfig_AdaptiveValues() {
+ final String adaptiveSuffix = "_adaptive";
+ Policy policy = Policy.fromSettings(
+ "", "",
+ new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_BATTERY_SAVER)
+ .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR, .5f)
+ .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED, false)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP, false)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER, false)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE, false)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE, false)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY, false)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK, false)
+ .setInt(BatterySaverPolicy.KEY_LOCATION_MODE,
+ PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF)
+ .setFloat(BatterySaverPolicy.KEY_ADJUST_BRIGHTNESS_FACTOR + adaptiveSuffix,
+ .9f)
+ .setBoolean(BatterySaverPolicy.KEY_ADVERTISE_IS_ENABLED + adaptiveSuffix,
+ true)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_FULL_BACKUP + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_DEFER_KEYVALUE_BACKUP + adaptiveSuffix,
+ true)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_ANIMATION + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_AOD + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_LAUNCH_BOOST + adaptiveSuffix,
+ true)
+ .setBoolean(
+ BatterySaverPolicy.KEY_DISABLE_OPTIONAL_SENSORS + adaptiveSuffix,
+ true)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_SOUNDTRIGGER + adaptiveSuffix,
+ true)
+ .setBoolean(BatterySaverPolicy.KEY_DISABLE_VIBRATION + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_BRIGHTNESS_ADJUSTMENT
+ + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_DATASAVER + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_FIREWALL + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_NIGHT_MODE + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_ENABLE_QUICK_DOZE + adaptiveSuffix, true)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_ALL_APPS_STANDBY + adaptiveSuffix,
+ true)
+ .setBoolean(BatterySaverPolicy.KEY_FORCE_BACKGROUND_CHECK + adaptiveSuffix,
+ true)
+ .setInt(BatterySaverPolicy.KEY_LOCATION_MODE + adaptiveSuffix,
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY)
+ .build(), adaptiveSuffix);
+ assertEquals(.9f, policy.adjustBrightnessFactor);
+ assertTrue(policy.advertiseIsEnabled);
+ assertTrue(policy.deferFullBackup);
+ assertTrue(policy.deferKeyValueBackup);
+ assertTrue(policy.disableAnimation);
+ assertTrue(policy.disableAod);
+ assertTrue(policy.disableLaunchBoost);
+ assertTrue(policy.disableOptionalSensors);
+ assertTrue(policy.disableSoundTrigger);
+ assertTrue(policy.disableVibration);
+ assertTrue(policy.enableAdjustBrightness);
+ assertTrue(policy.enableDataSaver);
+ assertTrue(policy.enableFirewall);
+ assertTrue(policy.enableNightMode);
+ assertTrue(policy.enableQuickDoze);
+ assertTrue(policy.forceAllAppsStandby);
+ assertTrue(policy.forceBackgroundCheck);
+ assertEquals(PowerManager.LOCATION_MODE_FOREGROUND_ONLY, policy.locationMode);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index 72d6caf1a5be..133f630b7a74 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.metrics.LogMaker;
+import android.util.IndentingPrintWriter;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -73,7 +74,7 @@ public class BatterySavingStatsTest {
void assertDumpable() {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
- dump(new PrintWriter(out), ""); // Just make sure it won't crash.
+ dump(new IndentingPrintWriter(new PrintWriter(out))); // Just make sure it won't crash.
}
void advanceClock(int minutes) {
diff --git a/services/tests/servicestests/src/com/android/server/tv/TvInputServiceManagerTest.java b/services/tests/servicestests/src/com/android/server/tv/TvInputServiceManagerTest.java
new file mode 100644
index 000000000000..229e27b9a438
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/TvInputServiceManagerTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 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.tv;
+
+import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING;
+import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN;
+import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING;
+import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+
+/**
+ * Tests for {@link TvInputManagerService}.
+ */
+@Presubmit
+@RunWith(JUnit4.class)
+public class TvInputServiceManagerTest {
+
+ @Test
+ public void getVideoUnavailableReasonForStatsd_tuning() {
+ assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd(
+ VIDEO_UNAVAILABLE_REASON_TUNING
+ )).isEqualTo(
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_TUNING);
+ }
+
+ @Test
+ public void getVideoUnavailableReasonForStatsd_unknown() {
+ assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd(
+ VIDEO_UNAVAILABLE_REASON_UNKNOWN
+ )).isEqualTo(
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN);
+ }
+
+
+ @Test
+ public void getVideoUnavailableReasonForStatsd_casBuffering() {
+ assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd(
+ VIDEO_UNAVAILABLE_REASON_BUFFERING
+ )).isEqualTo(
+ FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_BUFFERING);
+ }
+
+ @Test
+ public void getVideoUnavailableReasonForStatsd_casUnknown() {
+ assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd(
+ VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN
+ )).isEqualTo(
+ FrameworkStatsLog
+ .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN);
+ }
+
+ @Test
+ public void getVideoUnavailableReasonForStatsd_negative() {
+ assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd(
+ -1
+ )).isEqualTo(
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN);
+ }
+
+ @Test
+ public void getVideoUnavailableReasonForStatsd_oneBelow() {
+ assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd(
+ VIDEO_UNAVAILABLE_REASON_UNKNOWN - 1
+ )).isEqualTo(
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN);
+ }
+
+ @Test
+ public void getVideoUnavailableReasonForStatsd_oneAbove() {
+ assertThat(TvInputManagerService.getVideoUnavailableReasonForStatsd(
+ VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN + 1
+ )).isEqualTo(
+ FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN);
+ }
+
+}
diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
index 59cbd1ce1d7b..4c82818f71e4 100644
--- a/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
+++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/MockitoUtils.kt
@@ -17,6 +17,7 @@
package com.android.server.testutils
import org.mockito.Answers
+import org.mockito.ArgumentMatchers
import org.mockito.Mockito
import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
@@ -53,7 +54,7 @@ inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.ja
fun <T> spy(value: T, block: T.() -> Unit = {}) = Mockito.spy(value).apply(block)
-fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock)
+fun <Type> Stubber.whenever(mock: Type) = this.`when`(mock)
fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
@Suppress("UNCHECKED_CAST")
@@ -81,3 +82,5 @@ inline fun <reified T> spyThrowOnUnmocked(value: T?, block: T.() -> Unit = {}):
inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) =
spyThrowOnUnmocked<T>(null, block)
+
+inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java) \ No newline at end of file
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 00f706b13c58..e510b4fbfdd5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -153,7 +153,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
AccessibilityEvent.TYPES_ALL_MASK);
when(mAccessibilityService.addClient(any(), anyInt())).thenReturn(serviceReturnValue);
AccessibilityManager accessibilityManager =
- new AccessibilityManager(Handler.getMain(), mAccessibilityService, 0);
+ new AccessibilityManager(getContext(), Handler.getMain(), mAccessibilityService,
+ 0, true);
verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
assertTrue(accessibilityManager.isEnabled());
@@ -1191,7 +1192,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
NotificationRecord r = getLightsNotification();
mService.buzzBeepBlinkLocked(r);
verifyNeverLights();
- assertFalse(r.isInterruptive());
+ assertTrue(r.isInterruptive());
assertEquals(-1, r.getLastAudiblyAlertedMs());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index fdc089ee2f7e..9b37e76e4c65 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -102,7 +102,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
String key = mKeys[i];
Ranking ranking = new Ranking();
service.getCurrentRanking().getRanking(key, ranking);
- assertEquals(getVisibilityOverride(i), ranking.getVisibilityOverride());
+ assertEquals(getVisibilityOverride(i), ranking.getLockscreenVisibilityOverride());
assertEquals(getOverrideGroupKey(key), ranking.getOverrideGroupKey());
assertEquals(!isIntercepted(i), ranking.matchesInterruptionFilter());
assertEquals(getSuppressedVisualEffects(i), ranking.getSuppressedVisualEffects());
@@ -173,7 +173,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
tweak.getKey(),
tweak.getRank(),
!tweak.matchesInterruptionFilter(), // note the inversion here!
- tweak.getVisibilityOverride(),
+ tweak.getLockscreenVisibilityOverride(),
tweak.getSuppressedVisualEffects(),
tweak.getImportance(),
tweak.getImportanceExplanation(),
@@ -424,7 +424,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertEquals(comment, a.getKey(), b.getKey());
assertEquals(comment, a.getRank(), b.getRank());
assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter());
- assertEquals(comment, a.getVisibilityOverride(), b.getVisibilityOverride());
+ assertEquals(comment, a.getLockscreenVisibilityOverride(), b.getLockscreenVisibilityOverride());
assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects());
assertEquals(comment, a.getImportance(), b.getImportance());
assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation());
@@ -440,7 +440,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
assertEquals(comment, a.canBubble(), b.canBubble());
assertEquals(comment, a.isConversation(), b.isConversation());
- assertEquals(comment, a.getConversationShortcutInfo().getId(), b.getConversationShortcutInfo().getId());
+ assertEquals(comment, a.getConversationShortcutInfo().getId(),
+ b.getConversationShortcutInfo().getId());
assertActionsEqual(a.getSmartActions(), b.getSmartActions());
}
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 5cf529a239dc..762ec93712cb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -183,6 +183,7 @@ import com.android.server.notification.NotificationManagerService.NotificationAs
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -295,6 +296,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationHistoryManager mHistoryManager;
@Mock
StatsManager mStatsManager;
+ @Mock
+ MultiRateLimiter mToastRateLimiter;
BroadcastReceiver mPackageIntentReceiver;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
@@ -486,7 +489,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mGroupHelper, mAm, mAtm, mAppUsageStats,
mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
mAppOpsManager, mUm, mHistoryManager, mStatsManager,
- mock(TelephonyManager.class), mAmi);
+ mock(TelephonyManager.class), mAmi, mToastRateLimiter);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mService.setAudioManager(mAudioManager);
@@ -566,7 +569,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
try {
mService.onDestroy();
- } catch (IllegalStateException e) {
+ } catch (IllegalStateException | IllegalArgumentException e) {
// can throw if a broadcast receiver was never registered
}
@@ -4888,6 +4891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4910,6 +4914,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4928,6 +4933,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4950,10 +4956,32 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testToastRateLimiterCanPreventsShowCallForCustomToast() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ setToastRateIsWithinQuota(false); // rate limit reached
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ setAppInForegroundForToasts(mUid, true);
+
+ Binder token = new Binder();
+ ITransientNotification callback = mock(ITransientNotification.class);
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ nmService.enqueueToast(testPackage, token, callback, 2000, 0);
+ verify(callback, times(0)).show(any());
+ }
+
+ @Test
public void testAllowForegroundTextToasts() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4972,6 +5000,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4990,6 +5019,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5013,11 +5043,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testToastRateLimiterCanPreventsShowCallForTextToast() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ setToastRateIsWithinQuota(false); // rate limit reached
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ Binder token = new Binder();
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
+ verify(mStatusBar, times(0))
+ .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
+ }
+
+ @Test
public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws
Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = true;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5042,6 +5092,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5062,6 +5113,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5081,6 +5133,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5097,6 +5150,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5117,6 +5171,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5139,6 +5194,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = true;
+ setToastRateIsWithinQuota(true);
// package is suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5161,6 +5217,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
+ setToastRateIsWithinQuota(true);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5188,6 +5245,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mAtm.hasResumedActivity(uid)).thenReturn(inForeground);
}
+ private void setToastRateIsWithinQuota(boolean isWithinQuota) {
+ when(mToastRateLimiter.isWithinQuota(
+ anyInt(),
+ anyString(),
+ eq(NotificationManagerService.TOAST_QUOTA_TAG)))
+ .thenReturn(isWithinQuota);
+ }
+
@Test
public void testOnPanelRevealedAndHidden() {
int items = 5;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 98c4a2da6a4f..4d2a4784b5d9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -97,6 +97,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Pair;
import android.util.StatsEvent;
import android.util.TypedXmlPullParser;
@@ -3320,7 +3321,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setImportantConversation(true);
mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
- List<ConversationChannelWrapper> convos = mHelper.getConversations(false);
+ List<ConversationChannelWrapper> convos =
+ mHelper.getConversations(IntArray.wrap(new int[] {0}), false);
assertEquals(3, convos.size());
assertTrue(conversationWrapperContainsChannel(convos, channel));
@@ -3329,6 +3331,44 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testGetConversations_multiUser() {
+ String convoId = "convo";
+ NotificationChannel messages =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+
+ NotificationChannel messagesUser10 =
+ new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(
+ PKG_O, UID_O + UserHandle.PER_USER_RANGE, messagesUser10, true, false);
+
+ NotificationChannel messagesFromB =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ messagesFromB.setConversationId(messages.getId(), "different convo");
+ mHelper.createNotificationChannel(PKG_O, UID_O, messagesFromB, true, false);
+
+ NotificationChannel messagesFromBUser10 =
+ new NotificationChannel("B person msgs", "messages from B", IMPORTANCE_DEFAULT);
+ messagesFromBUser10.setConversationId(messagesUser10.getId(), "different convo");
+ mHelper.createNotificationChannel(
+ PKG_O, UID_O + UserHandle.PER_USER_RANGE, messagesFromBUser10, true, false);
+
+
+ List<ConversationChannelWrapper> convos =
+ mHelper.getConversations(IntArray.wrap(new int[] {0}), false);
+
+ assertEquals(1, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, messagesFromB));
+
+ convos =
+ mHelper.getConversations(IntArray.wrap(new int[] {0, UserHandle.getUserId(UID_O + UserHandle.PER_USER_RANGE)}), false);
+
+ assertEquals(2, convos.size());
+ assertTrue(conversationWrapperContainsChannel(convos, messagesFromB));
+ assertTrue(conversationWrapperContainsChannel(convos, messagesFromBUser10));
+ }
+
+ @Test
public void testGetConversations_notDemoted() {
String convoId = "convo";
NotificationChannel messages =
@@ -3358,7 +3398,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setImportantConversation(true);
mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
- List<ConversationChannelWrapper> convos = mHelper.getConversations(false);
+ List<ConversationChannelWrapper> convos =
+ mHelper.getConversations(IntArray.wrap(new int[] {0}), false);
assertEquals(2, convos.size());
assertTrue(conversationWrapperContainsChannel(convos, channel));
@@ -3396,7 +3437,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel2.setConversationId(calls.getId(), convoId);
mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
- List<ConversationChannelWrapper> convos = mHelper.getConversations(true);
+ List<ConversationChannelWrapper> convos =
+ mHelper.getConversations(IntArray.wrap(new int[] {0}), true);
assertEquals(2, convos.size());
assertTrue(conversationWrapperContainsChannel(convos, channel));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index a80f62ab09ee..4ce237e3aadc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -67,6 +67,7 @@ import com.android.server.lights.LightsManager;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -156,7 +157,8 @@ public class RoleObserverTest extends UiServiceTestCase {
mock(UriGrantsManagerInternal.class),
mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
mock(StatsManager.class), mock(TelephonyManager.class),
- mock(ActivityManagerInternal.class));
+ mock(ActivityManagerInternal.class),
+ mock(MultiRateLimiter.class));
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index e5ae2d3f63ab..f43e5a83c95d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -27,8 +27,11 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.Person;
import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutQueryWrapper;
import android.content.pm.ShortcutServiceInternal;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -44,6 +47,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -60,6 +64,7 @@ public class ShortcutHelperTest extends UiServiceTestCase {
private static final String SHORTCUT_ID = "shortcut";
private static final String PKG = "pkg";
private static final String KEY = "key";
+ private static final Person PERSON = mock(Person.class);
@Mock
LauncherApps mLauncherApps;
@@ -78,6 +83,8 @@ public class ShortcutHelperTest extends UiServiceTestCase {
@Mock
ShortcutInfo mShortcutInfo;
+ @Captor private ArgumentCaptor<ShortcutQuery> mShortcutQueryCaptor;
+
ShortcutHelper mShortcutHelper;
@Before
@@ -298,6 +305,7 @@ public class ShortcutHelperTest extends UiServiceTestCase {
when(si.getUserId()).thenReturn(UserHandle.USER_SYSTEM);
when(si.isLongLived()).thenReturn(true);
when(si.isEnabled()).thenReturn(true);
+ when(si.getPersons()).thenReturn(new Person[]{PERSON});
ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
shortcuts.add(si);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
@@ -308,4 +316,23 @@ public class ShortcutHelperTest extends UiServiceTestCase {
assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM))
.isSameInstanceAs(si);
}
+
+ @Test
+ public void testGetValidShortcutInfo_hasGetPersonsDataFlag() {
+
+ ShortcutInfo info = mShortcutHelper.getValidShortcutInfo(
+ "a", "p", UserHandle.SYSTEM);
+ verify(mLauncherApps).getShortcuts(mShortcutQueryCaptor.capture(), any());
+ ShortcutQueryWrapper shortcutQuery =
+ new ShortcutQueryWrapper(mShortcutQueryCaptor.getValue());
+ assertThat(hasFlag(shortcutQuery.getQueryFlags(), ShortcutQuery.FLAG_GET_PERSONS_DATA))
+ .isTrue();
+ }
+
+ /**
+ * Returns {@code true} iff {@link ShortcutQuery}'s {@code queryFlags} has {@code flag} set.
+ */
+ private static boolean hasFlag(int queryFlags, int flag) {
+ return (queryFlags & flag) != 0;
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java
new file mode 100644
index 000000000000..3998129659c6
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/VisibilityExtractorTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2020 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.notification;
+
+import static android.app.Notification.VISIBILITY_PRIVATE;
+import static android.app.Notification.VISIBILITY_SECRET;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VisibilityExtractorTest extends UiServiceTestCase {
+
+ @Mock RankingConfig mConfig;
+ @Mock
+ DevicePolicyManager mDpm;
+
+ private String mPkg = "com.android.server.notification";
+ private int mId = 1001;
+ private String mTag = null;
+ private int mUid = 1000;
+ private int mPid = 2000;
+ private int mUser = ActivityManager.getCurrentUser();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(DevicePolicyManager.class, mDpm);
+ }
+
+ private NotificationRecord getNotificationRecord(int visibility) {
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+ channel.setLockscreenVisibility(visibility);
+ when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
+
+ final Builder builder = new Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ Notification n = builder.build();
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
+ mPid, n, UserHandle.of(mUser), null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ return r;
+ }
+
+ //
+ // Tests
+ //
+
+ @Test
+ public void testGlobalAllDpmAllChannelAll() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_NO_OVERRIDE, r.getPackageVisibilityOverride());
+ }
+
+ @Test
+ public void testGlobalNoneDpmAllChannelAll() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(false);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride());
+ }
+
+ @Test
+ public void testGlobalSomeDpmAllChannelAll() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(false);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride());
+ }
+
+ @Test
+ public void testGlobalAllDpmNoneChannelAll() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(
+ KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride());
+ }
+
+ @Test
+ public void testGlobalAllDpmSomeChannelAll() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(
+ KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_NO_OVERRIDE);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride());
+ }
+
+ @Test
+ public void testGlobalAllDpmAllChannelNone() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_SECRET);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride());
+ }
+
+ @Test
+ public void testGlobalAllDpmAllChannelSome() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(0);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_PRIVATE);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_PRIVATE, r.getPackageVisibilityOverride());
+ }
+
+ @Test
+ public void testGlobalAllDpmSomeChannelNone() {
+ VisibilityExtractor extractor = new VisibilityExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ when(mConfig.canShowNotificationsOnLockscreen(mUser)).thenReturn(true);
+ when(mConfig.canShowPrivateNotificationsOnLockScreen(mUser)).thenReturn(true);
+
+ when(mDpm.getKeyguardDisabledFeatures(null, mUser)).thenReturn(
+ KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ NotificationRecord r = getNotificationRecord(VISIBILITY_SECRET);
+
+ extractor.process(r);
+
+ assertEquals(VISIBILITY_SECRET, r.getPackageVisibilityOverride());
+ }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index a4436951f48b..dd0c162d8467 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -75,7 +75,6 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
LocalServices.addService(UsageStatsManagerInternal.class,
mock(UsageStatsManagerInternal.class));
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
- mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
mContextSpy = spy(mContext);
mService = spy(new SliceManagerService(mContextSpy, TestableLooper.get(this).getLooper()));
@@ -90,6 +89,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
@Test
public void testAddPinCreatesPinned() throws RemoteException {
+ grantSlicePermission();
doReturn("pkg").when(mService).getDefaultHome(anyInt());
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
@@ -99,6 +99,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
@Test
public void testRemovePinDestroysPinned() throws RemoteException {
+ grantSlicePermission();
doReturn("pkg").when(mService).getDefaultHome(anyInt());
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
@@ -130,11 +131,13 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
@Test(expected = IllegalStateException.class)
public void testNoPinThrow() throws Exception {
+ grantSlicePermission();
mService.getPinnedSpecs(TEST_URI, "pkg");
}
@Test
public void testGetPinnedSpecs() throws Exception {
+ grantSlicePermission();
SliceSpec[] specs = new SliceSpec[] {
new SliceSpec("Something", 1) };
mService.pinSlice("pkg", TEST_URI, specs, mToken);
@@ -143,4 +146,10 @@ public class SliceManagerServiceTest extends UiServiceTestCase {
assertEquals(specs, mService.getPinnedSpecs(TEST_URI, "pkg"));
}
+ private void grantSlicePermission() {
+ doReturn(PERMISSION_GRANTED).when(mService).checkSlicePermission(
+ eq(TEST_URI), anyString(), anyString(), anyInt(), anyInt(), any());
+ doReturn(PERMISSION_GRANTED).when(mService).checkAccess(
+ anyString(), eq(TEST_URI), anyInt(), anyInt());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java
new file mode 100644
index 000000000000..75479de26b1d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyCombinationTests.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.KeyEvent.KEYCODE_POWER;
+import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class for {@link KeyCombinationManager}.
+ *
+ * Build/Install/Run:
+ * atest KeyCombinationTests
+ */
+
+@SmallTest
+public class KeyCombinationTests {
+ private KeyCombinationManager mKeyCombinationManager;
+
+ private boolean mAction1Triggered = false;
+ private boolean mAction2Triggered = false;
+ private boolean mAction3Triggered = false;
+
+ private boolean mPreCondition = true;
+ private static final long SCHEDULE_TIME = 300;
+
+ @Before
+ public void setUp() {
+ mKeyCombinationManager = new KeyCombinationManager();
+ initKeyCombinationRules();
+ }
+
+ private void initKeyCombinationRules() {
+ // Rule 1 : power + volume_down trigger action immediately.
+ mKeyCombinationManager.addRule(
+ new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN,
+ KEYCODE_POWER) {
+ @Override
+ void execute() {
+ mAction1Triggered = true;
+ }
+
+ @Override
+ void cancel() {
+ }
+ });
+
+ // Rule 2 : volume_up + volume_down with condition.
+ mKeyCombinationManager.addRule(
+ new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN,
+ KEYCODE_VOLUME_UP) {
+ @Override
+ boolean preCondition() {
+ return mPreCondition;
+ }
+
+ @Override
+ void execute() {
+ mAction2Triggered = true;
+ }
+
+ @Override
+ void cancel() {
+ }
+ });
+
+ // Rule 3 : power + volume_up schedule and trigger action after timeout.
+ mKeyCombinationManager.addRule(
+ new KeyCombinationManager.TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) {
+ final Runnable mAction = new Runnable() {
+ @Override
+ public void run() {
+ mAction3Triggered = true;
+ }
+ };
+ final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ void execute() {
+ mHandler.postDelayed(mAction, SCHEDULE_TIME);
+ }
+
+ @Override
+ void cancel() {
+ mHandler.removeCallbacks(mAction);
+ }
+ });
+ }
+
+ private void pressKeys(long firstKeyTime, int firstKeyCode, long secondKeyTime,
+ int secondKeyCode) {
+ pressKeys(firstKeyTime, firstKeyCode, secondKeyTime, secondKeyCode, 0);
+ }
+
+ private void pressKeys(long firstKeyTime, int firstKeyCode, long secondKeyTime,
+ int secondKeyCode, long pressTime) {
+ final KeyEvent firstKeyDown = new KeyEvent(firstKeyTime, firstKeyTime, ACTION_DOWN,
+ firstKeyCode, 0 /* repeat */, 0 /* metaState */);
+ final KeyEvent secondKeyDown = new KeyEvent(secondKeyTime, secondKeyTime, ACTION_DOWN,
+ secondKeyCode, 0 /* repeat */, 0 /* metaState */);
+
+ mKeyCombinationManager.interceptKey(firstKeyDown, true);
+ mKeyCombinationManager.interceptKey(secondKeyDown, true);
+
+ // keep press down.
+ try {
+ Thread.sleep(pressTime);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ final KeyEvent firstKeyUp = new KeyEvent(firstKeyTime, firstKeyTime, ACTION_UP,
+ firstKeyCode, 0 /* repeat */, 0 /* metaState */);
+ final KeyEvent secondKeyUp = new KeyEvent(secondKeyTime, secondKeyTime, ACTION_UP,
+ secondKeyCode, 0 /* repeat */, 0 /* metaState */);
+
+ mKeyCombinationManager.interceptKey(firstKeyUp, true);
+ mKeyCombinationManager.interceptKey(secondKeyUp, true);
+ }
+
+ @Test
+ public void testTriggerRule() {
+ final long eventTime = SystemClock.uptimeMillis();
+ pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
+ assertTrue(mAction1Triggered);
+
+ pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN);
+ assertTrue(mAction2Triggered);
+
+ pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP, SCHEDULE_TIME + 50);
+ assertTrue(mAction3Triggered);
+ }
+
+ /**
+ * Nothing should happen if there is no definition.
+ */
+ @Test
+ public void testNotTrigger_NoRule() {
+ final long eventTime = SystemClock.uptimeMillis();
+ pressKeys(eventTime, KEYCODE_BACK, eventTime, KEYCODE_VOLUME_DOWN);
+ assertFalse(mAction1Triggered);
+ assertFalse(mAction2Triggered);
+ assertFalse(mAction3Triggered);
+ }
+
+ /**
+ * Nothing should happen if the interval of press time is too long.
+ */
+ @Test
+ public void testNotTrigger_Interval() {
+ final long eventTime = SystemClock.uptimeMillis();
+ final long earlyEventTime = eventTime - 200; // COMBINE_KEY_DELAY_MILLIS = 150;
+ pressKeys(earlyEventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_DOWN);
+ assertFalse(mAction1Triggered);
+ }
+
+ /**
+ * Nothing should happen if the condition is false.
+ */
+ @Test
+ public void testNotTrigger_Condition() {
+ final long eventTime = SystemClock.uptimeMillis();
+ // we won't trigger action 2 because the condition is false.
+ mPreCondition = false;
+ pressKeys(eventTime, KEYCODE_VOLUME_UP, eventTime, KEYCODE_VOLUME_DOWN);
+ assertFalse(mAction2Triggered);
+ }
+
+ /**
+ * Nothing should happen if the keys released too early.
+ */
+ @Test
+ public void testNotTrigger_EarlyRelease() {
+ final long eventTime = SystemClock.uptimeMillis();
+ pressKeys(eventTime, KEYCODE_POWER, eventTime, KEYCODE_VOLUME_UP);
+ assertFalse(mAction3Triggered);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 3d31824e5aae..080f04efa9b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -80,13 +80,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
public void testActivityFinish() {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
- assertTrue("Activity must be finished", mAtm.finishActivity(activity.appToken,
- 0 /* resultCode */, null /* resultData */,
+ assertTrue("Activity must be finished", mAtm.mActivityClientController.finishActivity(
+ activity.appToken, 0 /* resultCode */, null /* resultData */,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY));
assertTrue(activity.finishing);
assertTrue("Duplicate activity finish request must also return 'true'",
- mAtm.finishActivity(activity.appToken, 0 /* resultCode */,
+ mAtm.mActivityClientController.finishActivity(activity.appToken, 0 /* resultCode */,
null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY));
}
@@ -225,7 +225,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
//to simulate NPE
doReturn(null).when(record).getParent();
- mAtm.enterPictureInPictureMode(token, params);
+ mAtm.mActivityClientController.enterPictureInPictureMode(token, params);
//if record's null parent is not handled gracefully, test will fail with NPE
mockSession.finishMocking();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index bc91c709aeb1..f536cd0b0ed4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -61,7 +61,7 @@ public class DisplayAreaGroupTest extends WindowTestsBase {
mTaskDisplayArea = new TaskDisplayArea(
mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1);
mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP);
- mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea);
+ mDisplayContent.onLastFocusedTaskDisplayAreaChanged(mTaskDisplayArea);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 8e6b6fab19eb..e47913fba7ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -20,6 +20,7 @@ import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -31,6 +32,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
@@ -45,6 +47,7 @@ import static org.testng.Assert.assertThrows;
import static java.util.stream.Collectors.toList;
import android.content.res.Resources;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
@@ -82,7 +85,7 @@ public class DisplayAreaPolicyBuilderTest {
private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null);
private WindowManagerService mWms;
private RootDisplayArea mRoot;
- private DisplayArea<WindowContainer> mImeContainer;
+ private DisplayArea.Tokens mImeContainer;
private DisplayContent mDisplayContent;
private TaskDisplayArea mDefaultTaskDisplayArea;
private List<TaskDisplayArea> mTaskDisplayAreaList;
@@ -95,7 +98,7 @@ public class DisplayAreaPolicyBuilderTest {
public void setup() {
mWms = mSystemServices.getWindowManagerService();
mRoot = new SurfacelessDisplayAreaRoot(mWms);
- mImeContainer = new DisplayArea<>(mWms, ABOVE_TASKS, "Ime");
+ mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer");
mDisplayContent = mock(DisplayContent.class);
mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks",
FEATURE_DEFAULT_TASK_CONTAINER);
@@ -113,11 +116,13 @@ public class DisplayAreaPolicyBuilderTest {
final Feature bar;
DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
- .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0)
+ .addFeature(foo = new Feature.Builder(mPolicy, "Foo",
+ FEATURE_VENDOR_FIRST)
.upTo(TYPE_STATUS_BAR)
.and(TYPE_NAVIGATION_BAR)
.build())
- .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1)
+ .addFeature(bar = new Feature.Builder(mPolicy, "Bar",
+ FEATURE_VENDOR_FIRST + 1)
.all()
.except(TYPE_STATUS_BAR)
.build())
@@ -148,6 +153,10 @@ public class DisplayAreaPolicyBuilderTest {
// The IME is below both foo and bar.
assertThat(fooDescendantMatcher.matches(mImeContainer)).isTrue();
assertThat(barDescendantMatcher.matches(mImeContainer)).isTrue();
+ assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD)))
+ .isEqualTo(mImeContainer);
+ assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD_DIALOG)))
+ .isEqualTo(mImeContainer);
List<DisplayArea<?>> actualOrder = collectLeafAreas(mRoot);
Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mImeContainer,
@@ -235,12 +244,14 @@ public class DisplayAreaPolicyBuilderTest {
final Feature other;
DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
- .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 0)
+ .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable",
+ FEATURE_VENDOR_FIRST)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build())
- .addFeature(other = new Feature.Builder(mPolicy, "Other", 1)
+ .addFeature(other = new Feature.Builder(mPolicy, "Other",
+ FEATURE_VENDOR_FIRST + 1)
.all()
.build())
.setImeContainer(mImeContainer)
@@ -308,9 +319,7 @@ public class DisplayAreaPolicyBuilderTest {
builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
mGroupRoot1)
.setImeContainer(mImeContainer)
- .setTaskDisplayAreas(Lists.newArrayList(
- new TaskDisplayArea(mDisplayContent, mWms, "testTda",
- FEATURE_VENDOR_FIRST + 1))));
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1)));
assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
@@ -335,11 +344,144 @@ public class DisplayAreaPolicyBuilderTest {
.setTaskDisplayAreas(mTaskDisplayAreaList));
builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
mGroupRoot2)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1)));
+
+ builder4.build(mWms);
+ }
+
+ @Test
+ public void testBuilder_rootHasUniqueId() {
+ // Root must have different id from all roots.
+ final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder();
+ builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+ final RootDisplayArea groupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1",
+ mRoot.mFeatureId);
+ builder1.addDisplayAreaGroupHierarchy(
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(groupRoot1)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1)));
+
+ assertThrows(IllegalStateException.class, () -> builder1.build(mWms));
+
+ // Root must have different id from all TDAs.
+ final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
+ builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
.setTaskDisplayAreas(Lists.newArrayList(
+ mDefaultTaskDisplayArea,
new TaskDisplayArea(mDisplayContent, mWms, "testTda",
- FEATURE_VENDOR_FIRST + 1))));
+ mRoot.mFeatureId))));
- builder4.build(mWms);
+ assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
+
+ // Root must have different id from all features.
+ final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder();
+ builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList)
+ .addFeature(new Feature.Builder(mPolicy, "testFeature", mRoot.mFeatureId)
+ .all()
+ .build()));
+
+ assertThrows(IllegalStateException.class, () -> builder3.build(mWms));
+ }
+
+ @Test
+ public void testBuilder_taskDisplayAreaHasUniqueId() {
+ // TDA must have different id from all TDAs.
+ final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder();
+ final List<TaskDisplayArea> tdaList = Lists.newArrayList(
+ mDefaultTaskDisplayArea,
+ mTda1,
+ new TaskDisplayArea(mDisplayContent, mWms, "tda2", mTda1.mFeatureId));
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(tdaList));
+
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // TDA must have different id from all features.
+ final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
+ builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ mDefaultTaskDisplayArea,
+ mTda1))
+ .addFeature(new Feature.Builder(mPolicy, "testFeature", mTda1.mFeatureId)
+ .all()
+ .build()));
+
+ assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
+ }
+
+ @Test
+ public void testBuilder_featureHasUniqueId() {
+ // Feature must have different id from features below the same root.
+ final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder();
+ builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList)
+ .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10)
+ .all()
+ .build())
+ .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10)
+ .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ .build()));
+
+ assertThrows(IllegalStateException.class, () -> builder.build(mWms));
+
+ // Features below different root can have the same id.
+ final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
+ builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList)
+ .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10)
+ .all()
+ .build()));
+ builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
+ mGroupRoot1)
+ .setTaskDisplayAreas(Lists.newArrayList(mTda1))
+ .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10)
+ .all()
+ .build()));
+
+ builder2.build(mWms);
+ }
+
+ @Test
+ public void testBuilder_idsNotGreaterThanFeatureVendorLast() {
+ // Root id should not be greater than FEATURE_VENDOR_LAST.
+ final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder();
+ final RootDisplayArea root = new SurfacelessDisplayAreaRoot(mWms, "testRoot",
+ FEATURE_VENDOR_LAST + 1);
+ builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList));
+
+ assertThrows(IllegalStateException.class, () -> builder1.build(mWms));
+
+ // TDA id should not be greater than FEATURE_VENDOR_LAST.
+ final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
+ builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(Lists.newArrayList(
+ mDefaultTaskDisplayArea,
+ new TaskDisplayArea(mDisplayContent, mWms, "testTda",
+ FEATURE_VENDOR_LAST + 1))));
+
+ assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
+
+ // Feature id should not be greater than FEATURE_VENDOR_LAST.
+ final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder();
+ builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+ .setImeContainer(mImeContainer)
+ .setTaskDisplayAreas(mTaskDisplayAreaList)
+ .addFeature(new Feature.Builder(mPolicy, "testFeature", FEATURE_VENDOR_LAST + 1)
+ .all()
+ .build()));
+
+ assertThrows(IllegalStateException.class, () -> builder3.build(mWms));
}
@Test
@@ -444,8 +586,8 @@ public class DisplayAreaPolicyBuilderTest {
.setTaskDisplayAreas(mTaskDisplayAreaList))
.addDisplayAreaGroupHierarchy(hierarchy1)
.addDisplayAreaGroupHierarchy(hierarchy2)
- .setSelectRootForWindowFunc((token, options) -> {
- if (token.windowType == TYPE_STATUS_BAR) {
+ .setSelectRootForWindowFunc((type, options) -> {
+ if (type == TYPE_STATUS_BAR) {
return mGroupRoot1;
}
return mGroupRoot2;
@@ -547,7 +689,7 @@ public class DisplayAreaPolicyBuilderTest {
private Map<DisplayArea<?>, Set<Integer>> calculateZSets(
DisplayAreaPolicyBuilder.Result policy,
- DisplayArea<WindowContainer> ime,
+ DisplayArea.Tokens ime,
DisplayArea<Task> tasks) {
Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>();
int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION,
@@ -599,10 +741,9 @@ public class DisplayAreaPolicyBuilderTest {
}
private WindowToken tokenOfType(int type) {
- WindowToken m = mock(WindowToken.class);
- when(m.getWindowLayerFromType()).thenReturn(
- mPolicy.getWindowLayerFromTypeLw(type, false /* canAddInternalSystemWindow */));
- return m;
+ WindowToken token = new WindowToken(mWms, new Binder(), type, false /* persistOnEmpty */,
+ mDisplayContent, false /* ownerCanManageAppTokens */);
+ return token;
}
private static void assertMatchLayerOrder(List<DisplayArea<?>> actualOrder,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
index 5a47493c12cd..496b2b744712 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -69,7 +69,7 @@ public class DisplayAreaPolicyTests {
WindowManagerService wms = mSystemServices.getWindowManagerService();
mRoot = new SurfacelessDisplayAreaRoot(wms);
spyOn(mRoot);
- DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
+ DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime");
DisplayContent displayContent = mock(DisplayContent.class);
doReturn(true).when(displayContent).isTrusted();
mTaskDisplayArea1 = new TaskDisplayArea(displayContent, wms, "Tasks1",
@@ -143,7 +143,7 @@ public class DisplayAreaPolicyTests {
FEATURE_VENDOR_FIRST + 5);
final TaskDisplayArea taskDisplayArea5 = new TaskDisplayArea(displayContent, wms, "Tasks5",
FEATURE_VENDOR_FIRST + 6);
- final DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
+ final DisplayArea.Tokens ime = new DisplayArea.Tokens(wms, ABOVE_TASKS, "Ime");
final DisplayAreaPolicy policy = new DisplayAreaPolicyBuilder()
.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
.setImeContainer(ime)
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index 351426ae78b2..2d289808dd26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -81,7 +81,7 @@ public class DisplayAreaProviderTest {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
- RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer) {
+ RootDisplayArea root, DisplayArea.Tokens imeContainer) {
throw new RuntimeException("test stub");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 025c5a6bb180..6f5a874114ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -458,8 +458,7 @@ public class DisplayAreaTest extends WindowTestsBase {
@Test
public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() {
- final TaskDisplayArea tda =
- mDisplayContent.getDefaultTaskDisplayArea();
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
final Task stack =
new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
final ActivityRecord activity = stack.getTopNonFinishingActivity();
@@ -478,6 +477,27 @@ public class DisplayAreaTest extends WindowTestsBase {
verify(mDisplayContent).onDescendantOrientationChanged(any());
}
+ @Test
+ public void testSetIgnoreOrientationRequest_updateOrientationRequestingTaskDisplayArea() {
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack =
+ new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
+ final ActivityRecord activity = stack.getTopNonFinishingActivity();
+
+ mDisplayContent.setFocusedApp(activity);
+ assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
+
+ // TDA is no longer handling orientation request, clear the last focused TDA.
+ tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isNull();
+
+ // TDA now handles orientation request, update last focused TDA based on the focused app.
+ tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+
+ assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
+ }
+
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds) {
super(wms, ANY, "half display area");
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 554160ccbb82..7c4f7db48022 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -16,7 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -28,7 +30,6 @@ import static org.junit.Assert.assertTrue;
import android.annotation.Nullable;
import android.platform.test.annotations.Presubmit;
import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
import android.util.Xml;
import android.view.Display;
import android.view.DisplayAddress;
@@ -69,7 +70,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir();
- private TestStorage mBaseSettingsStorage;
+ private TestStorage mDefaultVendorSettingsStorage;
+ private TestStorage mSecondaryVendorSettingsStorage;
private TestStorage mOverrideSettingsStorage;
private DisplayContent mPrimaryDisplay;
@@ -79,7 +81,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
public void setUp() throws Exception {
deleteRecursively(TEST_FOLDER);
- mBaseSettingsStorage = new TestStorage();
+ mDefaultVendorSettingsStorage = new TestStorage();
+ mSecondaryVendorSettingsStorage = new TestStorage();
mOverrideSettingsStorage = new TestStorage();
mPrimaryDisplay = mWm.getDefaultDisplayContentLocked();
@@ -122,7 +125,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
// Update settings with new value, should trigger write to injector.
DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
- mBaseSettingsStorage, mOverrideSettingsStorage);
+ mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
SettingsEntry overrideSettings = provider.getOverrideSettings(mPrimaryDisplayInfo);
overrideSettings.mForcedDensity = 200;
provider.updateOverrideSettings(mPrimaryDisplayInfo, overrideSettings);
@@ -162,12 +165,55 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
}
@Test
+ public void testReadingDisplaySettingsFromStorage_secondayVendorDisplaySettingsLocation() {
+ final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId;
+ prepareSecondaryDisplaySettings(displayIdentifier);
+
+ final DisplayWindowSettingsProvider provider =
+ new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage,
+ mOverrideSettingsStorage);
+
+ // Expected settings should be empty because the default is to read from the primary vendor
+ // settings location.
+ SettingsEntry expectedSettings = new SettingsEntry();
+ assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo()));
+
+ // Now switch to secondary vendor settings and assert proper settings.
+ provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage);
+ expectedSettings.mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo()));
+
+ // Switch back to primary and assert settings are empty again.
+ provider.setBaseSettingsStorage(mDefaultVendorSettingsStorage);
+ expectedSettings.mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo()));
+ }
+
+ @Test
+ public void testReadingDisplaySettingsFromStorage_overrideSettingsTakePrecedenceOverVendor() {
+ final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId;
+ prepareOverrideDisplaySettings(displayIdentifier);
+ prepareSecondaryDisplaySettings(displayIdentifier);
+
+ final DisplayWindowSettingsProvider provider =
+ new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage,
+ mOverrideSettingsStorage);
+ provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage);
+
+ // The windowing mode should be set to WINDOWING_MODE_PINNED because the override settings
+ // take precedence over the vendor provided settings.
+ SettingsEntry expectedSettings = new SettingsEntry();
+ expectedSettings.mWindowingMode = WINDOWING_MODE_PINNED;
+ assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo()));
+ }
+
+ @Test
public void testWritingDisplaySettingsToStorage() throws Exception {
final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo();
// Write some settings to storage.
DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
- mBaseSettingsStorage, mOverrideSettingsStorage);
+ mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
overrideSettings.mShouldShowSystemDecors = true;
overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
@@ -195,7 +241,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
// Write some settings to storage.
DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider(
- mBaseSettingsStorage, mOverrideSettingsStorage);
+ mDefaultVendorSettingsStorage, mOverrideSettingsStorage);
SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
overrideSettings.mShouldShowSystemDecors = true;
overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
@@ -236,10 +282,29 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase {
mOverrideSettingsStorage.setReadStream(is);
}
+ /**
+ * Prepares display settings and stores in {@link #mSecondaryVendorSettingsStorage}. Uses
+ * provided display identifier and stores windowingMode=WINDOWING_MODE_FULLSCREEN.
+ */
+ private void prepareSecondaryDisplaySettings(String displayIdentifier) {
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-settings>\n";
+ if (displayIdentifier != null) {
+ contents += " <display\n"
+ + " name=\"" + displayIdentifier + "\"\n"
+ + " windowingMode=\"" + WINDOWING_MODE_FULLSCREEN + "\"/>\n";
+ }
+ contents += "</display-settings>\n";
+
+ final InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mSecondaryVendorSettingsStorage.setReadStream(is);
+ }
+
private void readAndAssertExpectedSettings(DisplayContent displayContent,
SettingsEntry expectedSettings) {
final DisplayWindowSettingsProvider provider =
- new DisplayWindowSettingsProvider(mBaseSettingsStorage, mOverrideSettingsStorage);
+ new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage,
+ mOverrideSettingsStorage);
assertEquals(expectedSettings, provider.getSettings(displayContent.getDisplayInfo()));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
new file mode 100644
index 000000000000..eebc207d2d4f
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.SizeCompatTests.prepareUnresizable;
+import static com.android.server.wm.SizeCompatTests.rotateDisplay;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for the Dual DisplayAreaGroup device behavior.
+ *
+ * Build/Install/Run:
+ * atest WmTests:DualDisplayAreaGroupPolicyTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
+ private static final int FEATURE_FIRST_ROOT = FEATURE_VENDOR_FIRST;
+ private static final int FEATURE_FIRST_TASK_CONTAINER = FEATURE_DEFAULT_TASK_CONTAINER;
+ private static final int FEATURE_SECOND_ROOT = FEATURE_VENDOR_FIRST + 1;
+ private static final int FEATURE_SECOND_TASK_CONTAINER = FEATURE_VENDOR_FIRST + 2;
+
+ private DualDisplayContent mDisplay;
+ private DisplayAreaGroup mFirstRoot;
+ private DisplayAreaGroup mSecondRoot;
+ private TaskDisplayArea mFirstTda;
+ private TaskDisplayArea mSecondTda;
+ private Task mFirstTask;
+ private Task mSecondTask;
+ private ActivityRecord mFirstActivity;
+ private ActivityRecord mSecondActivity;
+
+ @Before
+ public void setUp() {
+ // Let the Display to be created with the DualDisplay policy.
+ final DisplayAreaPolicy.Provider policyProvider = new DualDisplayTestPolicyProvider();
+ doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
+
+ // Display: 1920x1200 (landscape). First and second display are both 860x1200 (portrait).
+ mDisplay = (DualDisplayContent) new DualDisplayContent.Builder(mAtm, 1920, 1200).build();
+ mFirstRoot = mDisplay.mFirstRoot;
+ mSecondRoot = mDisplay.mSecondRoot;
+ mFirstTda = mDisplay.getTaskDisplayArea(FEATURE_FIRST_TASK_CONTAINER);
+ mSecondTda = mDisplay.getTaskDisplayArea(FEATURE_SECOND_TASK_CONTAINER);
+ mFirstTask = new TaskBuilder(mSupervisor)
+ .setTaskDisplayArea(mFirstTda)
+ .setCreateActivity(true)
+ .build()
+ .getBottomMostTask();
+ mSecondTask = new TaskBuilder(mSupervisor)
+ .setTaskDisplayArea(mSecondTda)
+ .setCreateActivity(true)
+ .build()
+ .getBottomMostTask();
+ mFirstActivity = mFirstTask.getTopNonFinishingActivity();
+ mSecondActivity = mSecondTask.getTopNonFinishingActivity();
+
+ spyOn(mDisplay);
+ spyOn(mFirstRoot);
+ spyOn(mSecondRoot);
+ }
+
+ @Test
+ public void testNotIgnoreOrientationRequest_differentOrientationFromDisplay_reversesRequest() {
+ mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
+ assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+ assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
+ }
+
+ @Test
+ public void testNotIgnoreOrientationRequest_onlyRespectsFocusedTaskDisplayArea() {
+ mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ // Second TDA is not focused, so Display won't get the request
+ prepareUnresizable(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
+
+ // First TDA is focused, so Display gets the request
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ @Test
+ public void testIgnoreOrientationRequest_displayDoesNotReceiveOrientationChange() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ verify(mFirstRoot).onDescendantOrientationChanged(any());
+ verify(mDisplay, never()).onDescendantOrientationChanged(any());
+ }
+
+ @Test
+ public void testLaunchPortraitApp_fillsDisplayAreaGroup() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT);
+ final Rect dagBounds = new Rect(mFirstRoot.getBounds());
+ final Rect taskBounds = new Rect(mFirstTask.getBounds());
+ final Rect activityBounds = new Rect(mFirstActivity.getBounds());
+
+ // DAG is portrait (860x1200), so Task and Activity fill DAG.
+ assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
+ assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
+ assertThat(taskBounds).isEqualTo(dagBounds);
+ assertThat(activityBounds).isEqualTo(taskBounds);
+ }
+
+ @Test
+ public void testLaunchPortraitApp_sizeCompatAfterRotation() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT);
+ final Rect dagBounds = new Rect(mFirstRoot.getBounds());
+ final Rect activityBounds = new Rect(mFirstActivity.getBounds());
+
+ rotateDisplay(mDisplay, ROTATION_90);
+ final Rect newDagBounds = new Rect(mFirstRoot.getBounds());
+ final Rect newTaskBounds = new Rect(mFirstTask.getBounds());
+ final Rect activitySizeCompatBounds = new Rect(mFirstActivity.getBounds());
+ final Rect activityConfigBounds =
+ new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds());
+
+ // DAG is landscape (1200x860), Task fills parent
+ assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
+ assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
+ assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
+ assertThat(newDagBounds.height()).isEqualTo(dagBounds.width());
+ assertThat(newTaskBounds).isEqualTo(newDagBounds);
+
+ // Activity config bounds is unchanged, size compat bounds is (860x[860x860/1200=616])
+ assertThat(mFirstActivity.getSizeCompatScale()).isLessThan(1f);
+ assertThat(activityConfigBounds.width()).isEqualTo(activityBounds.width());
+ assertThat(activityConfigBounds.height()).isEqualTo(activityBounds.height());
+ assertThat(activitySizeCompatBounds.height()).isEqualTo(newTaskBounds.height());
+ assertThat(activitySizeCompatBounds.width()).isEqualTo(
+ newTaskBounds.height() * newTaskBounds.height() / newTaskBounds.width());
+ }
+
+ @Test
+ public void testLaunchLandscapeApp_taskIsLetterboxInDisplayAreaGroup() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ final Rect dagBounds = new Rect(mFirstRoot.getBounds());
+ final Rect taskBounds = new Rect(mFirstTask.getBounds());
+ final Rect activityBounds = new Rect(mFirstActivity.getBounds());
+
+ // DAG is portrait (860x1200), so Task is letterbox (860x[860x860/1200=616])
+ assertThat(mFirstTask.isTaskLetterboxed()).isTrue();
+ assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
+ assertThat(taskBounds.width()).isEqualTo(dagBounds.width());
+ assertThat(taskBounds.height())
+ .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height());
+ assertThat(activityBounds).isEqualTo(taskBounds);
+ }
+
+ @Test
+ public void testLaunchLandscapeApp_taskLetterboxBecomesActivityLetterboxAfterRotation() {
+ mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
+
+ prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ final Rect dagBounds = new Rect(mFirstRoot.getBounds());
+ final Rect activityBounds = new Rect(mFirstActivity.getBounds());
+
+ rotateDisplay(mDisplay, ROTATION_90);
+ final Rect newDagBounds = new Rect(mFirstRoot.getBounds());
+ final Rect newTaskBounds = new Rect(mFirstTask.getBounds());
+ final Rect newActivityBounds = new Rect(mFirstActivity.getBounds());
+
+ // DAG is landscape (1200x860), Task fills parent
+ // Task letterbox size
+ assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
+ assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
+ assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
+ assertThat(newDagBounds.height()).isEqualTo(dagBounds.width());
+ assertThat(newTaskBounds).isEqualTo(newDagBounds);
+
+ // Because we don't scale up, there is no size compat bounds and app bounds is the same as
+ // the previous bounds.
+ assertThat(mFirstActivity.hasSizeCompatBounds()).isFalse();
+ assertThat(newActivityBounds.width()).isEqualTo(activityBounds.width());
+ assertThat(newActivityBounds.height()).isEqualTo(activityBounds.height());
+ }
+
+ /** Display with two {@link DisplayAreaGroup}. Each of them take half of the screen. */
+ private static class DualDisplayContent extends TestDisplayContent {
+ final DisplayAreaGroup mFirstRoot;
+ final DisplayAreaGroup mSecondRoot;
+ final Rect mLastDisplayBounds;
+
+ /** Please use the {@link Builder} to create. */
+ DualDisplayContent(RootWindowContainer rootWindowContainer,
+ Display display) {
+ super(rootWindowContainer, display);
+
+ mFirstRoot = getGroupRoot(FEATURE_FIRST_ROOT);
+ mSecondRoot = getGroupRoot(FEATURE_SECOND_ROOT);
+ mLastDisplayBounds = new Rect(getBounds());
+ updateDisplayAreaGroupBounds();
+ }
+
+ DisplayAreaGroup getGroupRoot(int rootFeatureId) {
+ DisplayArea da = getDisplayArea(rootFeatureId);
+ assertThat(da).isInstanceOf(DisplayAreaGroup.class);
+ return (DisplayAreaGroup) da;
+ }
+
+ TaskDisplayArea getTaskDisplayArea(int tdaFeatureId) {
+ DisplayArea da = getDisplayArea(tdaFeatureId);
+ assertThat(da).isInstanceOf(TaskDisplayArea.class);
+ return (TaskDisplayArea) da;
+ }
+
+ DisplayArea getDisplayArea(int featureId) {
+ final DisplayArea displayArea =
+ getItemFromDisplayAreas(da -> da.mFeatureId == featureId ? da : null);
+ assertThat(displayArea).isNotNull();
+ return displayArea;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newParentConfig) {
+ super.onConfigurationChanged(newParentConfig);
+
+ final Rect curBounds = getBounds();
+ if (mLastDisplayBounds != null && !mLastDisplayBounds.equals(curBounds)) {
+ mLastDisplayBounds.set(curBounds);
+ updateDisplayAreaGroupBounds();
+ }
+ }
+
+ /** Updates first and second {@link DisplayAreaGroup} to take half of the screen. */
+ private void updateDisplayAreaGroupBounds() {
+ if (mFirstRoot == null || mSecondRoot == null) {
+ return;
+ }
+
+ final Rect bounds = mLastDisplayBounds;
+ Rect groupBounds1, groupBounds2;
+ if (bounds.width() >= bounds.height()) {
+ groupBounds1 = new Rect(bounds.left, bounds.top,
+ (bounds.right + bounds.left) / 2, bounds.bottom);
+
+ groupBounds2 = new Rect((bounds.right + bounds.left) / 2, bounds.top,
+ bounds.right, bounds.bottom);
+ } else {
+ groupBounds1 = new Rect(bounds.left, bounds.top,
+ bounds.right, (bounds.top + bounds.bottom) / 2);
+
+ groupBounds2 = new Rect(bounds.left,
+ (bounds.top + bounds.bottom) / 2, bounds.right, bounds.bottom);
+ }
+ mFirstRoot.setBounds(groupBounds1);
+ mSecondRoot.setBounds(groupBounds2);
+ }
+
+ static class Builder extends TestDisplayContent.Builder {
+
+ Builder(ActivityTaskManagerService service, int width, int height) {
+ super(service, width, height);
+ }
+
+ @Override
+ TestDisplayContent createInternal(Display display) {
+ return new DualDisplayContent(mService.mRootWindowContainer, display);
+ }
+ }
+ }
+
+ /** Policy to create a dual {@link DisplayAreaGroup} policy in test. */
+ private static class DualDisplayTestPolicyProvider implements DisplayAreaPolicy.Provider {
+
+ @Override
+ public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
+ RootDisplayArea root, DisplayArea.Tokens imeContainer) {
+ // Root
+ // Include FEATURE_WINDOWED_MAGNIFICATION because it will be used as the screen rotation
+ // layer
+ DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
+ .setImeContainer(imeContainer)
+ .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(
+ wmService.mPolicy,
+ "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION)
+ .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+ .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
+ .build());
+
+ // First
+ final RootDisplayArea firstRoot = new DisplayAreaGroup(wmService, "FirstRoot",
+ FEATURE_FIRST_ROOT);
+ final TaskDisplayArea firstTaskDisplayArea = new TaskDisplayArea(content, wmService,
+ "FirstTaskDisplayArea", FEATURE_FIRST_TASK_CONTAINER);
+ final List<TaskDisplayArea> firstTdaList = new ArrayList<>();
+ firstTdaList.add(firstTaskDisplayArea);
+ DisplayAreaPolicyBuilder.HierarchyBuilder firstHierarchy =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot)
+ .setTaskDisplayAreas(firstTdaList);
+
+ // Second
+ final RootDisplayArea secondRoot = new DisplayAreaGroup(wmService, "SecondRoot",
+ FEATURE_SECOND_ROOT);
+ final TaskDisplayArea secondTaskDisplayArea = new TaskDisplayArea(content, wmService,
+ "SecondTaskDisplayArea", FEATURE_SECOND_TASK_CONTAINER);
+ final List<TaskDisplayArea> secondTdaList = new ArrayList<>();
+ secondTdaList.add(secondTaskDisplayArea);
+ DisplayAreaPolicyBuilder.HierarchyBuilder secondHierarchy =
+ new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot)
+ .setTaskDisplayAreas(secondTdaList);
+
+ return new DisplayAreaPolicyBuilder()
+ .setRootHierarchy(rootHierarchy)
+ .addDisplayAreaGroupHierarchy(firstHierarchy)
+ .addDisplayAreaGroupHierarchy(secondHierarchy)
+ .build(wmService);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 91cfd4e6a89d..59d195b670a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -18,7 +18,9 @@ package com.android.server.wm;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.graphics.PixelFormat;
@@ -64,4 +66,22 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase {
mImeProvider.scheduleShowImePostLayout(target);
assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame());
}
+
+ @Test
+ public void testIsImeShowing() {
+ WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ makeWindowVisibleAndDrawn(ime);
+ mImeProvider.setWindow(ime, null, null);
+
+ WindowState target = createWindow(null, TYPE_APPLICATION, "app");
+ mDisplayContent.mInputMethodTarget = target;
+ mDisplayContent.mInputMethodControlTarget = target;
+
+ mImeProvider.scheduleShowImePostLayout(target);
+ assertFalse(mImeProvider.isImeShowing());
+ mImeProvider.checkShowImePostLayout();
+ assertTrue(mImeProvider.isImeShowing());
+ mImeProvider.setImeShowing(false);
+ assertFalse(mImeProvider.isImeShowing());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index da00198030e4..dd6e490b8282 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -92,7 +92,7 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mVisibleRequested = true;
mActivity.setSavedState(null /* savedState */);
mActivity.setState(Task.ActivityState.RESUMED, "testRestart");
- prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
resizeDisplay(mTask.mDisplayContent, 600, 1200);
@@ -115,7 +115,7 @@ public class SizeCompatTests extends WindowTestsBase {
// Put app window into freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
- prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertEquals(bounds, mActivity.getBounds());
// The activity should be able to accept negative x position [-150, 100 - 150, 600].
@@ -145,7 +145,7 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
final float aspectRatio = 1.2f;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = aspectRatio;
- prepareUnresizable(-1f, SCREEN_ORIENTATION_UNSPECIFIED);
+ prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
// The parent configuration doesn't change since the first resolved configuration, so the
@@ -205,7 +205,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
- prepareUnresizable(-1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
final Rect origBounds = new Rect(mActivity.getBounds());
@@ -262,7 +262,7 @@ public class SizeCompatTests extends WindowTestsBase {
setUpDisplaySizeWithApp(displayWidth, 1000);
final float maxAspect = 1.5f;
- prepareUnresizable(maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
+ prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
final Rect bounds = mActivity.getBounds();
@@ -286,7 +286,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testAspectRatioMatchParentBoundsAndImeAttachable() {
setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2000)
.setSystemDecorations(true).build());
- prepareUnresizable(2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ prepareUnresizable(mActivity, 2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
assertFitted();
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
@@ -307,7 +307,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testMoveToDifferentOrientDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
- prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
@@ -352,7 +352,7 @@ public class SizeCompatTests extends WindowTestsBase {
setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
.setNotch(notchHeight).build());
// Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
- prepareUnresizable(1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
@@ -381,7 +381,7 @@ public class SizeCompatTests extends WindowTestsBase {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
- prepareUnresizable(maxAspect, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_PORTRAIT);
// The display aspect ratio 2.5 > 1.4 (max of activity), so the size is fitted.
assertFitted();
@@ -415,7 +415,7 @@ public class SizeCompatTests extends WindowTestsBase {
Configuration c = new Configuration(mTask.getRequestedOverrideConfiguration());
c.screenLayout = fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
mTask.onRequestedOverrideConfigurationChanged(c);
- prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
+ prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
// The initial configuration should inherit from parent.
assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR,
@@ -433,7 +433,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testResetNonVisibleActivity() {
setUpDisplaySizeWithApp(1000, 2500);
- prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
+ prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
final DisplayContent display = mTask.mDisplayContent;
// Resize the display so the activity is in size compatibility mode.
resizeDisplay(display, 900, 1800);
@@ -472,7 +472,7 @@ public class SizeCompatTests extends WindowTestsBase {
setUpDisplaySizeWithApp(1000, 2000);
ActivityRecord activity = mActivity;
activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
- prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
final ArrayList<IBinder> compatTokens = new ArrayList<>();
@@ -545,7 +545,7 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
mActivity.mDisplayContent.mOpeningApps.add(mActivity);
final float maxAspect = 1.8f;
- prepareUnresizable(maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
+ prepareUnresizable(mActivity, maxAspect, SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
assertTrue(mActivity.isFixedRotationTransforming());
@@ -576,7 +576,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(statusBarController.isTransparentAllowed(w));
// Make the activity fill the display.
- prepareUnresizable(10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
+ prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
// Refresh the letterbox.
mActivity.mRootWindowContainer.performSurfacePlacement();
@@ -593,11 +593,11 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
- prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- final Rect displayBounds = mActivity.mDisplayContent.getBounds();
- final Rect taskBounds = mTask.getBounds();
- final Rect activityBounds = mActivity.getBounds();
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect taskBounds = new Rect(mTask.getBounds());
+ final Rect activityBounds = new Rect(mActivity.getBounds());
// Display shouldn't be rotated.
assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
@@ -622,15 +622,15 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
- prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- final Rect activityBounds = mActivity.getBounds();
+ final Rect activityBounds = new Rect(mActivity.getBounds());
// Rotate display to portrait.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- final Rect displayBounds = mActivity.mDisplayContent.getBounds();
- final Rect newActivityBounds = mActivity.getBounds();
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect newActivityBounds = new Rect(mActivity.getBounds());
assertTrue(displayBounds.width() < displayBounds.height());
// App should be in size compat.
@@ -647,10 +647,10 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
- prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
- Rect displayBounds = mActivity.mDisplayContent.getBounds();
- Rect activityBounds = mActivity.getBounds();
+ Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ Rect activityBounds = new Rect(mActivity.getBounds());
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
@@ -660,8 +660,8 @@ public class SizeCompatTests extends WindowTestsBase {
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- displayBounds = mActivity.mDisplayContent.getBounds();
- activityBounds = mActivity.getBounds();
+ displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ activityBounds = new Rect(mActivity.getBounds());
assertTrue(displayBounds.width() > displayBounds.height());
// App should be in size compat.
@@ -682,7 +682,7 @@ public class SizeCompatTests extends WindowTestsBase {
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
- prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
@@ -701,9 +701,9 @@ public class SizeCompatTests extends WindowTestsBase {
verify(mTask).onDescendantOrientationChanged(same(newActivity));
verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
- final Rect displayBounds = display.getBounds();
- final Rect taskBounds = mTask.getBounds();
- final Rect newActivityBounds = newActivity.getBounds();
+ final Rect displayBounds = new Rect(display.getBounds());
+ final Rect taskBounds = new Rect(mTask.getBounds());
+ final Rect newActivityBounds = new Rect(newActivity.getBounds());
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
@@ -722,7 +722,7 @@ public class SizeCompatTests extends WindowTestsBase {
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app without max aspect.
- prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
@@ -742,9 +742,9 @@ public class SizeCompatTests extends WindowTestsBase {
verify(mTask).onDescendantOrientationChanged(same(newActivity));
verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
- final Rect displayBounds = display.getBounds();
- final Rect taskBounds = mTask.getBounds();
- final Rect newActivityBounds = newActivity.getBounds();
+ final Rect displayBounds = new Rect(display.getBounds());
+ final Rect taskBounds = new Rect(mTask.getBounds());
+ final Rect newActivityBounds = new Rect(newActivity.getBounds());
// Task bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
assertTrue(mTask.isTaskLetterboxed());
@@ -765,7 +765,7 @@ public class SizeCompatTests extends WindowTestsBase {
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Portrait fixed app.
- prepareUnresizable(0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
clearInvocations(mActivity);
assertTrue(mTask.isTaskLetterboxed());
@@ -829,26 +829,31 @@ public class SizeCompatTests extends WindowTestsBase {
displayContent.getConfiguration().uiMode);
}
+ static void prepareUnresizable(ActivityRecord activity, int screenOrientation) {
+ prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation);
+ }
+
/**
- * Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or
+ * Setups {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or
* orientation.
*/
- private void prepareUnresizable(float maxAspect, int screenOrientation) {
- mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
- mActivity.mVisibleRequested = true;
+ static void prepareUnresizable(ActivityRecord activity, float maxAspect,
+ int screenOrientation) {
+ activity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ activity.mVisibleRequested = true;
if (maxAspect >= 0) {
- mActivity.info.maxAspectRatio = maxAspect;
+ activity.info.maxAspectRatio = maxAspect;
}
if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
- mActivity.info.screenOrientation = screenOrientation;
- mActivity.setRequestedOrientation(screenOrientation);
+ activity.info.screenOrientation = screenOrientation;
+ activity.setRequestedOrientation(screenOrientation);
}
// Make sure to use the provided configuration to construct the size compat fields.
- mActivity.clearSizeCompatMode();
- mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ activity.clearSizeCompatMode();
+ activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Make sure the display configuration reflects the change of activity.
- if (mActivity.mDisplayContent.updateOrientation()) {
- mActivity.mDisplayContent.sendNewConfiguration();
+ if (activity.mDisplayContent.updateOrientation()) {
+ activity.mDisplayContent.sendNewConfiguration();
}
}
@@ -869,7 +874,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertFalse(mActivity.hasSizeCompatBounds());
}
- private static Configuration rotateDisplay(DisplayContent display, int rotation) {
+ static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
display.computeScreenConfiguration(c);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 536ef48065a5..e1bc90a2551c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -299,6 +299,7 @@ public class SystemServicesTestRule implements TestRule {
doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(),
anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
spyOn(mWmService.mDisplayWindowSettings);
+ spyOn(mWmService.mDisplayWindowSettingsProvider);
// Setup factory classes to prevent calls to native code.
mTransaction = spy(StubTransaction.class);
@@ -324,7 +325,7 @@ public class SystemServicesTestRule implements TestRule {
final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
// Set the default focused TDA.
- display.setLastFocusedTaskDisplayArea(taskDisplayArea);
+ display.onLastFocusedTaskDisplayAreaChanged(taskDisplayArea);
spyOn(taskDisplayArea);
final Task homeStack = taskDisplayArea.getRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index d80f81642474..1c93e0f7e185 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -262,20 +262,50 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
// Activity on TDA1 is focused
mDisplayContent.setFocusedApp(firstActivity);
- assertThat(firstTaskDisplayArea.isLastFocused()).isTrue();
- assertThat(secondTaskDisplayArea.isLastFocused()).isFalse();
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse();
// No focused app, TDA1 is still recorded as last focused.
mDisplayContent.setFocusedApp(null);
- assertThat(firstTaskDisplayArea.isLastFocused()).isTrue();
- assertThat(secondTaskDisplayArea.isLastFocused()).isFalse();
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isTrue();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse();
// Activity on TDA2 is focused
mDisplayContent.setFocusedApp(secondActivity);
- assertThat(firstTaskDisplayArea.isLastFocused()).isFalse();
- assertThat(secondTaskDisplayArea.isLastFocused()).isTrue();
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue();
+ }
+
+ @Test
+ public void testIsLastFocused_onlyCountIfTaskDisplayAreaHandlesOrientationRequest() {
+ final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final Task firstStack = firstTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final Task secondStack = secondTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+ .setTask(firstStack).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+ .setTask(secondStack).build();
+ firstTaskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ secondTaskDisplayArea.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
+
+ // Activity on TDA1 is focused, but TDA1 doesn't respect orientation request
+ mDisplayContent.setFocusedApp(firstActivity);
+
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isFalse();
+
+ // Activity on TDA2 is focused, and TDA2 respects orientation request
+ mDisplayContent.setFocusedApp(secondActivity);
+
+ assertThat(firstTaskDisplayArea.canSpecifyOrientation()).isFalse();
+ assertThat(secondTaskDisplayArea.canSpecifyOrientation()).isTrue();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index e95efe785e8c..ae85ceb72958 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -37,7 +37,8 @@ import android.view.DisplayInfo;
class TestDisplayContent extends DisplayContent {
- private TestDisplayContent(RootWindowContainer rootWindowContainer, Display display) {
+ /** Please use the {@link Builder} to create, visible for use in test builder overrides only. */
+ TestDisplayContent(RootWindowContainer rootWindowContainer, Display display) {
super(display, rootWindowContainer);
// Normally this comes from display-properties as exposed by WM. Without that, just
// hard-code to FULLSCREEN for tests.
@@ -72,7 +73,7 @@ class TestDisplayContent extends DisplayContent {
private boolean mCanRotate = true;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
private int mPosition = POSITION_BOTTOM;
- private final ActivityTaskManagerService mService;
+ protected final ActivityTaskManagerService mService;
private boolean mSystemDecorations = false;
Builder(ActivityTaskManagerService service, int width, int height) {
@@ -125,14 +126,16 @@ class TestDisplayContent extends DisplayContent {
mInfo.logicalDensityDpi = dpi;
return this;
}
+ TestDisplayContent createInternal(Display display) {
+ return new TestDisplayContent(mService.mRootWindowContainer, display);
+ }
TestDisplayContent build() {
SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
final int displayId = SystemServicesTestRule.sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- final TestDisplayContent newDisplay =
- new TestDisplayContent(mService.mRootWindowContainer, display);
+ final TestDisplayContent newDisplay = createInternal(display);
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
@@ -158,7 +161,7 @@ class TestDisplayContent extends DisplayContent {
mService.mRootWindowContainer.addChild(newDisplay, mPosition);
// Set the default focused TDA.
- newDisplay.setLastFocusedTaskDisplayArea(newDisplay.getDefaultTaskDisplayArea());
+ newDisplay.onLastFocusedTaskDisplayAreaChanged(newDisplay.getDefaultTaskDisplayArea());
return newDisplay;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index c4bcfdbcf89d..b0b8afd6c3a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -948,6 +948,36 @@ public class WindowContainerTests extends WindowTestsBase {
assertFalse(act.isAnimating(PARENTS));
}
+ @Test
+ public void testRegisterWindowContainerListener() {
+ final WindowContainer container = new WindowContainer(mWm);
+ container.mDisplayContent = mDisplayContent;
+ final TestWindowContainerListener listener = new TestWindowContainerListener();
+ Configuration config = container.getConfiguration();
+ Rect bounds = new Rect(0, 0, 10, 10);
+ config.windowConfiguration.setBounds(bounds);
+ config.densityDpi = 100;
+ container.onRequestedOverrideConfigurationChanged(config);
+ container.registerWindowContainerListener(listener);
+
+ assertEquals(mDisplayContent, listener.mDisplayContent);
+ assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds());
+ assertEquals(100, listener.mConfiguration.densityDpi);
+
+ container.onDisplayChanged(mDefaultDisplay);
+ assertEquals(listener.mDisplayContent, mDefaultDisplay);
+
+ config = new Configuration();
+ bounds = new Rect(0, 0, 20, 20);
+ config.windowConfiguration.setBounds(bounds);
+ config.densityDpi = 200;
+
+ container.onRequestedOverrideConfigurationChanged(config);
+
+ assertEquals(bounds, listener.mConfiguration.windowConfiguration.getBounds());
+ assertEquals(200, listener.mConfiguration.densityDpi);
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
@@ -1131,4 +1161,19 @@ public class WindowContainerTests extends WindowTestsBase {
mSession.kill();
}
}
+
+ private static class TestWindowContainerListener implements WindowContainerListener {
+ private Configuration mConfiguration = new Configuration();
+ private DisplayContent mDisplayContent;
+
+ @Override
+ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ mConfiguration.setTo(overrideConfiguration);
+ }
+
+ @Override
+ public void onDisplayChanged(DisplayContent dc) {
+ mDisplayContent = dc;
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
new file mode 100644
index 000000000000..67067ee08e41
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.IWindowToken;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest WmTests:WindowContextListenerControllerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class WindowContextListenerControllerTests extends WindowTestsBase {
+ private WindowContextListenerController mController;
+
+ private static final int TEST_UID = 12345;
+ private static final int ANOTHER_UID = 1000;
+
+ private final IBinder mClientToken = new Binder();
+ private WindowContainer mContainer;
+
+ @Before
+ public void setUp() {
+ mController = new WindowContextListenerController();
+ mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+ }
+
+ @Test
+ public void testRegisterWindowContextListener() {
+ mController.registerWindowContainerListener(mClientToken, mContainer, -1);
+
+ assertEquals(1, mController.mListeners.size());
+
+ final IBinder clientToken = new Binder();
+ mController.registerWindowContainerListener(clientToken, mContainer, -1);
+
+ assertEquals(2, mController.mListeners.size());
+
+ final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ mDefaultDisplay);
+ mController.registerWindowContainerListener(mClientToken, container, -1);
+
+ // The number of listeners doesn't increase since the listener just gets updated.
+ assertEquals(2, mController.mListeners.size());
+
+ WindowContextListenerController.WindowContextListenerImpl listener =
+ mController.mListeners.get(mClientToken);
+ assertEquals(container, listener.getWindowContainer());
+ }
+
+ @Test
+ public void testRegisterWindowContextListenerClientConfigPropagation() {
+ final TestWindowTokenClient clientToken = new TestWindowTokenClient();
+
+ final Configuration config1 = mContainer.getConfiguration();
+ final Rect bounds1 = new Rect(0, 0, 10, 10);
+ config1.windowConfiguration.setBounds(bounds1);
+ config1.densityDpi = 100;
+ mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+ mController.registerWindowContainerListener(clientToken, mContainer, -1);
+
+ assertEquals(bounds1, clientToken.mConfiguration.windowConfiguration.getBounds());
+ assertEquals(config1.densityDpi, clientToken.mConfiguration.densityDpi);
+ assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);
+
+ // Update the WindowContainer.
+ final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ mDefaultDisplay);
+ final Configuration config2 = container.getConfiguration();
+ final Rect bounds2 = new Rect(0, 0, 20, 20);
+ config2.windowConfiguration.setBounds(bounds2);
+ config2.densityDpi = 200;
+ container.onRequestedOverrideConfigurationChanged(config2);
+
+ mController.registerWindowContainerListener(clientToken, container, -1);
+
+ assertEquals(bounds2, clientToken.mConfiguration.windowConfiguration.getBounds());
+ assertEquals(config2.densityDpi, clientToken.mConfiguration.densityDpi);
+ assertEquals(DEFAULT_DISPLAY, clientToken.mDisplayId);
+
+ // Update the configuration of WindowContainer.
+ container.onRequestedOverrideConfigurationChanged(config1);
+
+ assertEquals(bounds1, clientToken.mConfiguration.windowConfiguration.getBounds());
+ assertEquals(config1.densityDpi, clientToken.mConfiguration.densityDpi);
+
+ // Update the display of WindowContainer.
+ container.onDisplayChanged(mDisplayContent);
+
+ assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);
+ }
+
+ @Test
+ public void testCanCallerRemoveListener_NullListener_ReturnFalse() {
+ assertFalse(mController.assertCallerCanRemoveListener(mClientToken,
+ true /* callerCanManagerAppTokens */, TEST_UID));
+ }
+
+ @Test
+ public void testCanCallerRemoveListener_CanManageAppTokens_ReturnTrue() {
+ mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID);
+
+ assertTrue(mController.assertCallerCanRemoveListener(mClientToken,
+ true /* callerCanManagerAppTokens */, ANOTHER_UID));
+ }
+
+ @Test
+ public void testCanCallerRemoveListener_SameUid_ReturnTrue() {
+ mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID);
+
+ assertTrue(mController.assertCallerCanRemoveListener(mClientToken,
+ false /* callerCanManagerAppTokens */, TEST_UID));
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testCanCallerRemoveListener_DifferentUid_ThrowException() {
+ mController.registerWindowContainerListener(mClientToken, mContainer, TEST_UID);
+
+ mController.assertCallerCanRemoveListener(mClientToken,
+ false /* callerCanManagerAppTokens */, ANOTHER_UID);
+ }
+
+ private class TestWindowTokenClient extends IWindowToken.Stub {
+ private Configuration mConfiguration;
+ private int mDisplayId;
+ private boolean mRemoved;
+
+ @Override
+ public void onConfigurationChanged(Configuration configuration, int displayId) {
+ mConfiguration = configuration;
+ mDisplayId = displayId;
+ }
+
+ @Override
+ public void onWindowTokenRemoved() {
+ mRemoved = true;
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index ba144dd367d9..4e1b3510bdaa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -20,7 +20,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
-import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS;
+import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -58,8 +58,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
@Test
public void testForceDesktopModeOnExternalDisplays() {
- try (SettingsSession forceDesktopModeSession = new
- SettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) {
+ try (BoolSettingsSession forceDesktopModeSession = new
+ BoolSettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) {
final boolean forceDesktopMode = !forceDesktopModeSession.getSetting();
final Uri forceDesktopModeUri = forceDesktopModeSession.setSetting(forceDesktopMode);
mWm.mSettingsObserver.onChange(false, forceDesktopModeUri);
@@ -70,8 +70,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
@Test
public void testFreeformWindow() {
- try (SettingsSession freeformWindowSession = new
- SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) {
+ try (BoolSettingsSession freeformWindowSession = new
+ BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) {
final boolean curFreeformWindow = freeformWindowSession.getSetting();
final boolean newFreeformWindow = !curFreeformWindow;
final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow);
@@ -84,8 +84,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
@Test
public void testFreeformWindow_valueChanged_updatesDisplaySettings() {
- try (SettingsSession freeformWindowSession = new
- SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) {
+ try (BoolSettingsSession freeformWindowSession = new
+ BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) {
final boolean curFreeformWindow = freeformWindowSession.getSetting();
final boolean newFreeformWindow = !curFreeformWindow;
final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow);
@@ -106,8 +106,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
@Test
public void testForceResizableMode() {
- try (SettingsSession forceResizableSession = new
- SettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) {
+ try (BoolSettingsSession forceResizableSession = new
+ BoolSettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) {
final boolean forceResizableMode = !forceResizableSession.getSetting();
final Uri forceResizableUri = forceResizableSession.setSetting(forceResizableMode);
@@ -119,8 +119,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
@Test
public void testEnableSizeCompatFreeform() {
- try (SettingsSession enableSizeCompatFreeformSession = new
- SettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) {
+ try (BoolSettingsSession enableSizeCompatFreeformSession = new
+ BoolSettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) {
final boolean enableSizeCompatFreeform =
!enableSizeCompatFreeformSession.getSetting();
final Uri enableSizeCompatFreeformUri =
@@ -132,21 +132,22 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
}
@Test
- public void testEnabledIgnoreVendorDisplaySettings() {
- try (SettingsSession ignoreVendorDisplaySettingsSession = new
- SettingsSession(DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS)) {
- final boolean ignoreVendorDisplaySettings =
- !ignoreVendorDisplaySettingsSession.getSetting();
- final Uri ignoreVendorDisplaySettingUri =
- ignoreVendorDisplaySettingsSession.setSetting(ignoreVendorDisplaySettings);
+ public void testChangeBaseDisplaySettingsPath() {
+ try (StringSettingsSession baseDisplaySettingsPathSession = new
+ StringSettingsSession(DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH)) {
+ final String path = baseDisplaySettingsPathSession.getSetting() + "-test";
+ final Uri baseDisplaySettingsPathUri = baseDisplaySettingsPathSession.setSetting(path);
clearInvocations(mWm.mRoot);
clearInvocations(mWm.mDisplayWindowSettings);
+ clearInvocations(mWm.mDisplayWindowSettingsProvider);
- mWm.mSettingsObserver.onChange(false /* selfChange */, ignoreVendorDisplaySettingUri);
+ mWm.mSettingsObserver.onChange(false /* selfChange */, baseDisplaySettingsPathUri);
- assertEquals(mWm.mDisplayWindowSettingsProvider.getVendorSettingsIgnored(),
- ignoreVendorDisplaySettings);
+ ArgumentCaptor<String> pathCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mWm.mDisplayWindowSettingsProvider).setBaseSettingsFilePath(
+ pathCaptor.capture());
+ assertEquals(path, pathCaptor.getValue());
ArgumentCaptor<DisplayContent> captor =
ArgumentCaptor.forClass(DisplayContent.class);
@@ -161,14 +162,14 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
}
}
- private class SettingsSession implements AutoCloseable {
+ private class BoolSettingsSession implements AutoCloseable {
private static final int SETTING_VALUE_OFF = 0;
private static final int SETTING_VALUE_ON = 1;
private final String mSettingName;
private final int mInitialValue;
- SettingsSession(String name) {
+ BoolSettingsSession(String name) {
mSettingName = name;
mInitialValue = getSetting() ? SETTING_VALUE_ON : SETTING_VALUE_OFF;
}
@@ -192,4 +193,32 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
setSetting(mInitialValue == SETTING_VALUE_ON);
}
}
+
+ private class StringSettingsSession implements AutoCloseable {
+ private final String mSettingName;
+ private final String mInitialValue;
+
+ StringSettingsSession(String name) {
+ mSettingName = name;
+ mInitialValue = getSetting();
+ }
+
+ String getSetting() {
+ return Settings.Global.getString(getContentResolver(), mSettingName);
+ }
+
+ Uri setSetting(String value) {
+ Settings.Global.putString(getContentResolver(), mSettingName, value);
+ return Settings.Global.getUriFor(mSettingName);
+ }
+
+ private ContentResolver getContentResolver() {
+ return getInstrumentation().getTargetContext().getContentResolver();
+ }
+
+ @Override
+ public void close() {
+ setSetting(mInitialValue);
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index dba157e6ce06..f123bc16e52c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -824,7 +824,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
final PictureInPictureParams p = new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(1, 2)).build();
- assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
+ assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode(
+ record.token, p));
waitUntilHandlersIdle();
assertNotNull(o.mInfo);
assertNotNull(o.mInfo.pictureInPictureParams);
@@ -845,14 +846,15 @@ public class WindowOrganizerTests extends WindowTestsBase {
final ActivityRecord record = makePipableActivity();
final PictureInPictureParams p = new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(1, 2)).build();
- assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
+ assertTrue(mWm.mAtmService.mActivityClientController.enterPictureInPictureMode(
+ record.token, p));
waitUntilHandlersIdle();
assertNotNull(o.mInfo);
assertNotNull(o.mInfo.pictureInPictureParams);
final PictureInPictureParams p2 = new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(3, 4)).build();
- mWm.mAtmService.setPictureInPictureParams(record.token, p2);
+ mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2);
waitUntilHandlersIdle();
assertNotNull(o.mChangedInfo);
assertNotNull(o.mChangedInfo.pictureInPictureParams);
@@ -920,7 +922,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertTrue(stack2.isOrganized());
// Verify a back pressed does not call the organizer
- mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
verify(organizer, never()).onBackPressedOnTaskRoot(any());
// Enable intercepting back
@@ -928,7 +930,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
stack.mRemoteToken.toWindowContainerToken(), true);
// Verify now that the back press does call the organizer
- mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
// Disable intercepting back
@@ -936,7 +938,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
stack.mRemoteToken.toWindowContainerToken(), false);
// Verify now that the back press no longer calls the organizer
- mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 14a62d1a4ff0..d64b46d925ff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -49,6 +49,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -354,6 +355,13 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
}
+ static void makeWindowVisibleAndDrawn(WindowState... windows) {
+ makeWindowVisible(windows);
+ for (WindowState win : windows) {
+ win.mWinAnimator.mDrawState = HAS_DRAWN;
+ }
+ }
+
/** Creates a {@link TaskDisplayArea} right above the default one. */
static TaskDisplayArea createTaskDisplayArea(DisplayContent displayContent,
WindowManagerService service, String name, int displayAreaFeature) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index e0785c13a336..462df300fdfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -231,7 +231,7 @@ public class WindowTokenTests extends WindowTestsBase {
@Test
public void testWindowAttachedWithOptions() {
- BiFunction<WindowToken, Bundle, RootDisplayArea> selectFunc =
+ BiFunction<Integer, Bundle, RootDisplayArea> selectFunc =
((DisplayAreaPolicyBuilder.Result) mDisplayContent.mDisplayAreaPolicy)
.mSelectRootForWindowFunc;
spyOn(selectFunc);
@@ -241,7 +241,7 @@ public class WindowTokenTests extends WindowTestsBase {
true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
- verify(selectFunc).apply(token1, null);
+ verify(selectFunc).apply(token1.windowType, null);
final Bundle options = new Bundle();
final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
@@ -249,6 +249,6 @@ public class WindowTokenTests extends WindowTestsBase {
true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
false /* fromClientToken */, options /* options */);
- verify(selectFunc).apply(token2, options);
+ verify(selectFunc).apply(token2.windowType, options);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 06c23de84ac3..b5bb2a81a1cd 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -49,7 +49,6 @@ import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.media.permission.Identity;
-import android.media.permission.IdentityContext;
import android.media.permission.PermissionUtil;
import android.media.permission.SafeCloseable;
import android.os.Binder;
@@ -92,7 +91,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -141,10 +140,10 @@ public class VoiceInteractionManagerService extends SystemService {
mUserManagerInternal = Objects.requireNonNull(
LocalServices.getService(UserManagerInternal.class));
- PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
- PermissionManagerServiceInternal.class);
+ LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
+ LegacyPermissionManagerInternal.class);
permissionManagerInternal.setVoiceInteractionPackagesProvider(
- new PermissionManagerServiceInternal.PackagesProvider() {
+ new LegacyPermissionManagerInternal.PackagesProvider() {
@Override
public String[] getPackages(int userId) {
mServiceStub.initForUser(userId);
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 3104c7e7e0a1..1a0e5269d51a 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -34,6 +34,7 @@ import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.Log;
@@ -42,15 +43,20 @@ import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.BackgroundDexOptService;
+import com.android.server.pm.PackageManagerService;
import com.android.server.wm.ActivityMetricsLaunchObserver;
import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BooleanSupplier;
import java.util.HashMap;
+import java.util.List;
/**
* System-server-local proxy into the {@code IIorap} native service.
@@ -65,6 +71,7 @@ public class IorapForwardingService extends SystemService {
/** $> adb shell 'setprop iorapd.forwarding_service.wtf_crash true' */
private static boolean WTF_CRASH = SystemProperties.getBoolean(
"iorapd.forwarding_service.wtf_crash", false);
+ private static final Duration TIMEOUT = Duration.ofSeconds(600L);
// "Unique" job ID from the service name. Also equal to 283673059.
public static final int JOB_ID_IORAPD = encodeEnglishAlphabetStringIntoInt("iorapd");
@@ -80,6 +87,12 @@ public class IorapForwardingService extends SystemService {
private volatile IorapdJobService mJobService; // Write-once (null -> non-null forever).
private volatile static IorapForwardingService sSelfService; // Write once (null -> non-null).
+
+ /**
+ * Atomics set to true if the JobScheduler requests an abort.
+ */
+ private final AtomicBoolean mAbortIdleCompilation = new AtomicBoolean(false);
+
/**
* Initializes the system service.
* <p>
@@ -154,9 +167,27 @@ public class IorapForwardingService extends SystemService {
@VisibleForTesting
protected boolean isIorapEnabled() {
+ // These two mendel flags should match those in iorapd native process
+ // system/iorapd/src/common/property.h
+ boolean isTracingEnabled =
+ getMendelFlag("iorap_perfetto_enable", "iorapd.perfetto.enable", false);
+ boolean isReadAheadEnabled =
+ getMendelFlag("iorap_readahead_enable", "iorapd.readahead.enable", false);
// Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process
// never comes up, so all binder connections will fail indefinitely.
- return IS_ENABLED;
+ return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled);
+ }
+
+ private boolean getMendelFlag(String mendelFlag, String sysProperty, boolean defaultValue) {
+ // TODO(yawanng) use DeviceConfig to get mendel property.
+ // DeviceConfig doesn't work and the reason is not clear.
+ // Provider service is already up before IORapForwardService.
+ String mendelProperty = "persist.device_config."
+ + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT
+ + "."
+ + mendelFlag;
+ return SystemProperties.getBoolean(mendelProperty,
+ SystemProperties.getBoolean(sysProperty, defaultValue));
}
//</editor-fold>
@@ -239,7 +270,9 @@ public class IorapForwardingService extends SystemService {
//
// TODO: it would be good to get nodified of 'adb shell stop iorapd' to avoid
// printing this warning.
- Log.w(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime);
+ if (DEBUG) {
+ Log.v(TAG, "Failed to connect to iorapd, is it down? Delay for " + sleepTime);
+ }
// Use a handler instead of Thread#sleep to avoid backing up the binder thread
// when this is called from the death recipient callback.
@@ -275,7 +308,9 @@ public class IorapForwardingService extends SystemService {
// Connect to the native binder service.
mIorapRemote = provideIorapRemote();
if (mIorapRemote == null) {
- Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?");
+ if (DEBUG) {
+ Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?");
+ }
return false;
}
invokeRemote(mIorapRemote,
@@ -542,32 +577,86 @@ public class IorapForwardingService extends SystemService {
// Tell iorapd to start a background job.
Log.d(TAG, "Starting background job: " + params.toString());
- // We wait until that job's sequence ID returns to us with 'Completed',
- RequestId request;
- synchronized (mLock) {
- // TODO: would be cleaner if we got the request from the 'invokeRemote' function.
- // Better yet, consider a Pair<RequestId, Future<TaskResult>> or similar.
- request = RequestId.nextValueForSequence();
- mRunningJobs.put(request, params);
- }
+ mAbortIdleCompilation.set(false);
+ // PackageManagerService starts before IORap service.
+ PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
+ List<String> pkgs = pm.getAllPackages();
+ runIdleCompilationAsync(params, pkgs);
+ return true;
+ }
- if (!invokeRemote(mIorapRemote, (IIorap remote) ->
- remote.onJobScheduledEvent(request,
- JobScheduledEvent.createIdleMaintenance(
- JobScheduledEvent.TYPE_START_JOB,
- params))
- )) {
- synchronized (mLock) {
- mRunningJobs.remove(request); // Avoid memory leaks.
+ private void runIdleCompilationAsync(final JobParameters params, final List<String> pkgs) {
+ new Thread("IORap_IdleCompilation") {
+ @Override
+ public void run() {
+ for (int i = 0; i < pkgs.size(); i++) {
+ String pkg = pkgs.get(i);
+ if (mAbortIdleCompilation.get()) {
+ Log.i(TAG, "The idle compilation is aborted");
+ return;
+ }
+
+ // We wait until that job's sequence ID returns to us with 'Completed',
+ RequestId request;
+ synchronized (mLock) {
+ // TODO: would be cleaner if we got the request from the
+ // 'invokeRemote' function. Better yet, consider
+ // a Pair<RequestId, Future<TaskResult>> or similar.
+ request = RequestId.nextValueForSequence();
+ mRunningJobs.put(request, params);
+ }
+
+ Log.i(TAG, String.format("IORap compile package: %s, [%d/%d]",
+ pkg, i + 1, pkgs.size()));
+ boolean shouldUpdateVersions = (i == 0);
+ if (!invokeRemote(mIorapRemote, (IIorap remote) ->
+ remote.onJobScheduledEvent(request,
+ JobScheduledEvent.createIdleMaintenance(
+ JobScheduledEvent.TYPE_START_JOB,
+ params,
+ pkg,
+ shouldUpdateVersions)))) {
+ synchronized (mLock) {
+ mRunningJobs.remove(request); // Avoid memory leaks.
+ }
+ }
+
+ // Wait until the job is complete and removed from the running jobs.
+ retryWithTimeout(TIMEOUT, () -> {
+ synchronized (mLock) {
+ return !mRunningJobs.containsKey(request);
+ }
+ });
+ }
+
+ // Finish the job after all packages are compiled.
+ if (mProxy != null) {
+ mProxy.jobFinished(params, /*reschedule*/false);
+ }
}
+ }.start();
+ }
- // Something went wrong on the remote side. Treat the job as being
- // 'already finished' (i.e. immediately release wake lock).
- return false;
- }
+ /** Retry until timeout. */
+ private boolean retryWithTimeout(final Duration timeout, BooleanSupplier supplier) {
+ long totalSleepTimeMs = 0L;
+ long sleepIntervalMs = 10L;
+ while (true) {
+ if (supplier.getAsBoolean()) {
+ return true;
+ }
+ try {
+ TimeUnit.MILLISECONDS.sleep(sleepIntervalMs);
+ } catch (InterruptedException e) {
+ Log.e(TAG, e.getMessage());
+ return false;
+ }
- // True -> keep the wakelock acquired until #jobFinished is called.
- return true;
+ totalSleepTimeMs += sleepIntervalMs;
+ if (totalSleepTimeMs > timeout.toMillis()) {
+ return false;
+ }
+ }
}
// Called by system to prematurely stop the job.
@@ -575,32 +664,7 @@ public class IorapForwardingService extends SystemService {
public boolean onStopJob(JobParameters params) {
// As this is unexpected behavior, print a warning.
Log.w(TAG, "onStopJob(params=" + params.toString() + ")");
-
- // No longer track this job (avoids a memory leak).
- boolean wasTracking = false;
- synchronized (mLock) {
- for (HashMap.Entry<RequestId, JobParameters> entry : mRunningJobs.entrySet()) {
- if (entry.getValue().getJobId() == params.getJobId()) {
- mRunningJobs.remove(entry.getKey());
- wasTracking = true;
- }
- }
- }
-
- // Notify iorapd to stop (abort) the job.
- if (wasTracking) {
- invokeRemote(mIorapRemote, (IIorap remote) ->
- remote.onJobScheduledEvent(RequestId.nextValueForSequence(),
- JobScheduledEvent.createIdleMaintenance(
- JobScheduledEvent.TYPE_STOP_JOB,
- params))
- );
- } else {
- // Even weirder. This could only be considered "correct" if iorapd reported success
- // concurrently to the JobService requesting an onStopJob.
- Log.e(TAG, "Untracked onStopJob request"); // see above Log.w for the params.
- }
-
+ mAbortIdleCompilation.set(true);
// Yes, retry the job at a later time no matter what.
return true;
@@ -626,18 +690,6 @@ public class IorapForwardingService extends SystemService {
}
Log.d(TAG, "Finished background job: " + jobParameters.toString());
-
- // Job is successful and periodic. Do not 'reschedule' according to the back-off
- // criteria.
- //
- // This releases the wakelock that was acquired in #onStartJob.
-
- IorapdJobServiceProxy proxy = mProxy;
- if (proxy != null) {
- proxy.jobFinished(jobParameters, /*reschedule*/false);
- }
- // Cannot call 'jobFinished' on 'this' because it was not constructed
- // from the JobService, so it would get an NPE when calling mEngine.
}
public void onIorapdDisconnected() {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
index 2055b206dd7a..b91dd71fd28c 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
@@ -55,6 +55,10 @@ public class JobScheduledEvent implements Parcelable {
/** @see JobParameters#getJobId() */
public final int jobId;
+ public final String packageName;
+
+ public final boolean shouldUpdateVersions;
+
/** Device is 'idle' and it's charging (plugged in). */
public static final int SORT_IDLE_MAINTENANCE = 0;
private static final int SORT_MAX = 0;
@@ -77,14 +81,22 @@ public class JobScheduledEvent implements Parcelable {
* Only the job ID is retained from {@code jobParams}, all other param info is dropped.
*/
@NonNull
- public static JobScheduledEvent createIdleMaintenance(@Type int type, JobParameters jobParams) {
- return new JobScheduledEvent(type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE);
+ public static JobScheduledEvent createIdleMaintenance(
+ @Type int type, JobParameters jobParams, String packageName, boolean shouldUpdateVersions) {
+ return new JobScheduledEvent(
+ type, jobParams.getJobId(), SORT_IDLE_MAINTENANCE, packageName, shouldUpdateVersions);
}
- private JobScheduledEvent(@Type int type, int jobId, @Sort int sort) {
+ private JobScheduledEvent(@Type int type,
+ int jobId,
+ @Sort int sort,
+ String packageName,
+ boolean shouldUpdateVersions) {
this.type = type;
this.jobId = jobId;
this.sort = sort;
+ this.packageName = packageName;
+ this.shouldUpdateVersions = shouldUpdateVersions;
checkConstructorArguments();
}
@@ -108,12 +120,16 @@ public class JobScheduledEvent implements Parcelable {
private boolean equals(JobScheduledEvent other) {
return type == other.type &&
jobId == other.jobId &&
- sort == other.sort;
+ sort == other.sort &&
+ packageName.equals(other.packageName) &&
+ shouldUpdateVersions == other.shouldUpdateVersions;
}
@Override
public String toString() {
- return String.format("{type: %d, jobId: %d, sort: %d}", type, jobId, sort);
+ return String.format(
+ "{type: %d, jobId: %d, sort: %d, packageName: %s, shouldUpdateVersions %b}",
+ type, jobId, sort, packageName, shouldUpdateVersions);
}
//<editor-fold desc="Binder boilerplate">
@@ -122,6 +138,8 @@ public class JobScheduledEvent implements Parcelable {
out.writeInt(type);
out.writeInt(jobId);
out.writeInt(sort);
+ out.writeString(packageName);
+ out.writeBoolean(shouldUpdateVersions);
// We do not parcel the entire JobParameters here because there is no C++ equivalent
// of that class [which the iorapd side of the binder interface requires].
@@ -131,6 +149,8 @@ public class JobScheduledEvent implements Parcelable {
this.type = in.readInt();
this.jobId = in.readInt();
this.sort = in.readInt();
+ this.packageName = in.readString();
+ this.shouldUpdateVersions = in.readBoolean();
checkConstructorArguments();
}
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
index c9903f9fa901..1963ff3cabc7 100644
--- a/telecomm/TEST_MAPPING
+++ b/telecomm/TEST_MAPPING
@@ -23,7 +23,9 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
- },
+ }
+ ],
+ "presubmit-large": [
{
"name": "CtsTelecomTestCases",
"options": [
diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.java b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
new file mode 100644
index 000000000000..10339a818205
--- /dev/null
+++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the quality report that bluetooth framework sends
+ * whenever there's a bad voice quality is detected from their side.
+ * It is sent as part of a call event via {@link Call#sendCallEvent(String, Bundle)}
+ * associated with extra EXTRA_BLUETOOTH_CALL_QUALITY_REPORT.
+ * Note that this report will be sent only during an active voice/voip call.
+ * @hide
+ */
+@SystemApi
+public final class BluetoothCallQualityReport implements Parcelable {
+
+ /**
+ * Event that is sent via {@link Call#sendCallEvent(String, Bundle)} for a call quality report
+ */
+ public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT =
+ "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT";
+
+ /**
+ * Extra key sent with {@link Call#sendCallEvent(String, Bundle)}
+ */
+ public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT =
+ "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT";
+
+ private final long mSentTimestampMillis;
+ private final boolean mChoppyVoice;
+ private final int mRssiDbm;
+ private final int mSnrDb;
+ private final int mRetransmittedPacketsCount;
+ private final int mPacketsNotReceivedCount;
+ private final int mNegativeAcknowledgementCount;
+
+ /**
+ * @return Time in milliseconds since the epoch. Designates when report was sent.
+ * Used to determine whether this report arrived too late to be useful.
+ */
+ public @ElapsedRealtimeLong long getSentTimestampMillis() {
+ return mSentTimestampMillis;
+ }
+
+ /**
+ * @return {@code true} if bluetooth hardware detects voice is choppy
+ */
+ public boolean isChoppyVoice() {
+ return mChoppyVoice;
+ }
+
+ /**
+ * @return Received Signal Strength Indication (RSSI) value in dBm.
+ * This value shall be an absolute received signal strength value.
+ */
+ public @IntRange(from = -127, to = 20) int getRssiDbm() {
+ return mRssiDbm;
+ }
+
+ /**
+ * @return Signal-to-Noise Ratio (SNR) value in dB.
+ * The controller shall provide the average SNR of all the channels currently used by the link.
+ */
+ public int getSnrDb() {
+ return mSnrDb;
+ }
+
+ /**
+ * @return The number of retransmissions since the last event.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getRetransmittedPacketsCount() {
+ return mRetransmittedPacketsCount;
+ }
+
+ /**
+ * @return No RX count since the last event.
+ * The count increases when no packet is received at the scheduled time slot or the received
+ * packet is corrupted.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getPacketsNotReceivedCount() {
+ return mPacketsNotReceivedCount;
+ }
+
+ /**
+ * @return NAK (Negative Acknowledge) count since the last event.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getNegativeAcknowledgementCount() {
+ return mNegativeAcknowledgementCount;
+ }
+
+ //
+ // Parcelable implementation
+ //
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeLong(mSentTimestampMillis);
+ out.writeBoolean(mChoppyVoice);
+ out.writeInt(mRssiDbm);
+ out.writeInt(mSnrDb);
+ out.writeInt(mRetransmittedPacketsCount);
+ out.writeInt(mPacketsNotReceivedCount);
+ out.writeInt(mNegativeAcknowledgementCount);
+ }
+
+ public static final @android.annotation.NonNull Creator<BluetoothCallQualityReport> CREATOR =
+ new Creator<BluetoothCallQualityReport>() {
+ @Override
+ public BluetoothCallQualityReport createFromParcel(Parcel in) {
+ return new BluetoothCallQualityReport(in);
+ }
+
+ @Override
+ public BluetoothCallQualityReport[] newArray(int size) {
+ return new BluetoothCallQualityReport[size];
+ }
+ };
+
+ /**
+ * Builder class for {@link ConnectionRequest}
+ */
+ public static final class Builder {
+ private long mSentTimestampMillis;
+ private boolean mChoppyVoice;
+ private int mRssiDbm;
+ private int mSnrDb;
+ private int mRetransmittedPacketsCount;
+ private int mPacketsNotReceivedCount;
+ private int mNegativeAcknowledgementCount;
+
+ public Builder() { }
+
+ /**
+ * Set the time when report was sent in milliseconds since the epoch.
+ * @param sentTimestampMillis
+ */
+ public @NonNull Builder setSentTimestampMillis(long sentTimestampMillis) {
+ mSentTimestampMillis = sentTimestampMillis;
+ return this;
+ }
+
+ /**
+ * Set if bluetooth hardware detects voice is choppy
+ * @param choppyVoice
+ */
+ public @NonNull Builder setChoppyVoice(boolean choppyVoice) {
+ mChoppyVoice = choppyVoice;
+ return this;
+ }
+
+ /**
+ * Set Received Signal Strength Indication (RSSI) value in dBm.
+ * @param rssiDbm
+ */
+ public @NonNull Builder setRssiDbm(int rssiDbm) {
+ mRssiDbm = rssiDbm;
+ return this;
+ }
+
+ /**
+ * Set Signal-to-Noise Ratio (SNR) value in dB.
+ * @param snrDb
+ */
+ public @NonNull Builder setSnrDb(int snrDb) {
+ mSnrDb = snrDb;
+ return this;
+ }
+
+ /**
+ * Set The number of retransmissions since the last event.
+ * @param retransmittedPacketsCount
+ */
+ public @NonNull Builder setRetransmittedPacketsCount(
+ int retransmittedPacketsCount) {
+ mRetransmittedPacketsCount = retransmittedPacketsCount;
+ return this;
+ }
+
+ /**
+ * Set No RX count since the last event.
+ * @param packetsNotReceivedCount
+ */
+ public @NonNull Builder setPacketsNotReceivedCount(
+ int packetsNotReceivedCount) {
+ mPacketsNotReceivedCount = packetsNotReceivedCount;
+ return this;
+ }
+
+ /**
+ * Set NAK (Negative Acknowledge) count since the last event.
+ * @param negativeAcknowledgementCount
+ */
+ public @NonNull Builder setNegativeAcknowledgementCount(
+ int negativeAcknowledgementCount) {
+ mNegativeAcknowledgementCount = negativeAcknowledgementCount;
+ return this;
+ }
+
+ /**
+ * Build the {@link BluetoothCallQualityReport}
+ * @return Result of the builder
+ */
+ public @NonNull BluetoothCallQualityReport build() {
+ return new BluetoothCallQualityReport(this);
+ }
+ }
+
+ private BluetoothCallQualityReport(Parcel in) {
+ mSentTimestampMillis = in.readLong();
+ mChoppyVoice = in.readBoolean();
+ mRssiDbm = in.readInt();
+ mSnrDb = in.readInt();
+ mRetransmittedPacketsCount = in.readInt();
+ mPacketsNotReceivedCount = in.readInt();
+ mNegativeAcknowledgementCount = in.readInt();
+ }
+
+ private BluetoothCallQualityReport(Builder builder) {
+ mSentTimestampMillis = builder.mSentTimestampMillis;
+ mChoppyVoice = builder.mChoppyVoice;
+ mRssiDbm = builder.mRssiDbm;
+ mSnrDb = builder.mSnrDb;
+ mRetransmittedPacketsCount = builder.mRetransmittedPacketsCount;
+ mPacketsNotReceivedCount = builder.mPacketsNotReceivedCount;
+ mNegativeAcknowledgementCount = builder.mNegativeAcknowledgementCount;
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 960b0df40061..5b03863efc7d 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -268,10 +268,69 @@ public class TelecomManager {
/**
* Optional extra for {@link android.content.Intent#ACTION_CALL} containing a string call
* subject which will be associated with an outgoing call. Should only be specified if the
- * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}.
+ * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}
+ * or {@link PhoneAccount#CAPABILITY_CALL_COMPOSER}.
*/
public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
+ // Values for EXTRA_PRIORITY
+ /**
+ * Indicates the call composer call priority is normal.
+ *
+ * Reference: RCC.20 Section 2.4.4.2
+ */
+ public static final int PRIORITY_NORMAL = 0;
+
+ /**
+ * Indicates the call composer call priority is urgent.
+ *
+ * Reference: RCC.20 Section 2.4.4.2
+ */
+ public static final int PRIORITY_URGENT = 1;
+
+ /**
+ * Extra for the call composer call priority, either {@link #PRIORITY_NORMAL} or
+ * {@link #PRIORITY_URGENT}.
+ *
+ * Reference: RCC.20 Section 2.4.4.2
+ */
+ public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY";
+
+ /**
+ * Extra for the call composer call location, an {@link android.location.Location} parcelable
+ * class to represent the geolocation as a latitude and longitude pair.
+ *
+ * Reference: RCC.20 Section 2.4.3.2
+ */
+ public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION";
+
+ /**
+ * A boolean extra set on incoming calls to indicate that the call has a picture specified.
+ * Given that image download could take a (short) time, the EXTRA is set immediately upon
+ * adding the call to the Dialer app, this allows the Dialer app to reserve space for an image
+ * if one is expected. The EXTRA may be unset if the image download ends up failing for some
+ * reason.
+ */
+ public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
+
+ /**
+ * A URI representing the picture that was downloaded when a call is received.
+ * This is a content URI within the call log provider which can be used to open a file
+ * descriptor. This could be set a short time after a call is added to the Dialer app if the
+ * download is delayed for some reason. The Dialer app will receive a callback via
+ * {@link Call.Callback#onDetailsChanged} when this value has changed.
+ *
+ * Reference: RCC.20 Section 2.4.3.2
+ */
+ public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
+
+ // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture.
+ /**
+ * A ParcelUuid used as a token to represent a picture that was uploaded prior to the call
+ * being placed.
+ */
+ public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
+
/**
* The extra used by a {@link ConnectionService} to provide the handle of the caller that
* has initiated a new incoming call.
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index baee4736b527..0939bf02a039 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4012,6 +4012,17 @@ public class CarrierConfigManager {
"default_preferred_apn_name_string";
/**
+ * Indicates if the carrier supports call composer.
+ */
+ public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
+
+ /**
+ * Indicates the carrier server url that serves the call composer picture.
+ */
+ public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING =
+ "call_composer_picture_server_url_string";
+
+ /**
* For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values
* for IPv4 and IPv6 if both are sent.
* TODO: remove in later release
@@ -4563,6 +4574,8 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
+ sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index b381ccecde47..189a4b898886 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -21,7 +21,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.radio.V1_4.CellInfo.Info;
-import android.hardware.radio.V1_5.CellInfo.CellInfoRatSpecificInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -352,6 +351,13 @@ public abstract class CellInfo implements Parcelable {
}
/** @hide */
+ protected CellInfo(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ this.mRegistered = ci.registered;
+ this.mTimeStamp = timeStamp;
+ this.mCellConnectionStatus = ci.connectionStatus;
+ }
+
+ /** @hide */
public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) {
if (ci == null) return null;
switch(ci.cellInfoType) {
@@ -395,17 +401,49 @@ public abstract class CellInfo implements Parcelable {
public static CellInfo create(android.hardware.radio.V1_5.CellInfo ci, long timeStamp) {
if (ci == null) return null;
switch (ci.ratSpecificInfo.getDiscriminator()) {
- case CellInfoRatSpecificInfo.hidl_discriminator.gsm:
+ case android.hardware.radio.V1_5.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.gsm:
+ return new CellInfoGsm(ci, timeStamp);
+ case android.hardware.radio.V1_5.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.cdma:
+ return new CellInfoCdma(ci, timeStamp);
+ case android.hardware.radio.V1_5.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.lte:
+ return new CellInfoLte(ci, timeStamp);
+ case android.hardware.radio.V1_5.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.wcdma:
+ return new CellInfoWcdma(ci, timeStamp);
+ case android.hardware.radio.V1_5.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma:
+ return new CellInfoTdscdma(ci, timeStamp);
+ case android.hardware.radio.V1_5.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.nr:
+ return new CellInfoNr(ci, timeStamp);
+ default: return null;
+ }
+ }
+
+ /** @hide */
+ public static CellInfo create(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ if (ci == null) return null;
+ switch (ci.ratSpecificInfo.getDiscriminator()) {
+ case android.hardware.radio.V1_6.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.gsm:
return new CellInfoGsm(ci, timeStamp);
- case CellInfoRatSpecificInfo.hidl_discriminator.cdma:
+ case android.hardware.radio.V1_6.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.cdma:
return new CellInfoCdma(ci, timeStamp);
- case CellInfoRatSpecificInfo.hidl_discriminator.lte:
+ case android.hardware.radio.V1_6.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.lte:
return new CellInfoLte(ci, timeStamp);
- case CellInfoRatSpecificInfo.hidl_discriminator.wcdma:
+ case android.hardware.radio.V1_6.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.wcdma:
return new CellInfoWcdma(ci, timeStamp);
- case CellInfoRatSpecificInfo.hidl_discriminator.tdscdma:
+ case android.hardware.radio.V1_6.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.tdscdma:
return new CellInfoTdscdma(ci, timeStamp);
- case CellInfoRatSpecificInfo.hidl_discriminator.nr:
+ case android.hardware.radio.V1_6.CellInfo
+ .CellInfoRatSpecificInfo.hidl_discriminator.nr:
return new CellInfoNr(ci, timeStamp);
default: return null;
}
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 3ce99facfdb1..dbb30d29eb87 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -87,6 +87,15 @@ public final class CellInfoCdma extends CellInfo implements Parcelable {
new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
}
+ /** @hide */
+ public CellInfoCdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ super(ci, timeStamp);
+ final android.hardware.radio.V1_2.CellInfoCdma cic = ci.ratSpecificInfo.cdma();
+ mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+ mCellSignalStrengthCdma =
+ new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+ }
+
/**
* @return a {@link CellIdentityCdma} instance.
*/
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index e296e613362a..e1d996e87fcf 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -82,6 +82,14 @@ public final class CellInfoGsm extends CellInfo implements Parcelable {
mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
}
+ /** @hide */
+ public CellInfoGsm(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ super(ci, timeStamp);
+ final android.hardware.radio.V1_5.CellInfoGsm cig = ci.ratSpecificInfo.gsm();
+ mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+ mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
+ }
+
/**
* @return a {@link CellIdentityGsm} instance.
*/
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 6f812341756b..39b320afdac9 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -91,6 +91,15 @@ public final class CellInfoLte extends CellInfo implements Parcelable {
mCellConfig = new CellConfigLte();
}
+ /** @hide */
+ public CellInfoLte(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ super(ci, timeStamp);
+ final android.hardware.radio.V1_6.CellInfoLte cil = ci.ratSpecificInfo.lte();
+ mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte);
+ mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte);
+ mCellConfig = new CellConfigLte();
+ }
+
/**
* @return a {@link CellIdentityLte} instance.
*/
diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
index e01e8f0d5b51..12e6a38bfdc0 100644
--- a/telephony/java/android/telephony/CellInfoNr.java
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -68,6 +68,14 @@ public final class CellInfoNr extends CellInfo {
mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr);
}
+ /** @hide */
+ public CellInfoNr(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ super(ci, timeStamp);
+ final android.hardware.radio.V1_6.CellInfoNr cil = ci.ratSpecificInfo.nr();
+ mCellIdentity = new CellIdentityNr(cil.cellIdentityNr);
+ mCellSignalStrength = new CellSignalStrengthNr(cil.signalStrengthNr);
+ }
+
/**
* @return a {@link CellIdentityNr} instance.
*/
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index 038c49ac37ee..994b317a47fd 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -85,6 +85,14 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable {
mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
}
+ /** @hide */
+ public CellInfoTdscdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ super(ci, timeStamp);
+ final android.hardware.radio.V1_5.CellInfoTdscdma cit = ci.ratSpecificInfo.tdscdma();
+ mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+ mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+ }
+
/**
* @return a {@link CellIdentityTdscdma} instance.
*/
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index c74955f807b0..62ac0b8ae04e 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -80,6 +80,14 @@ public final class CellInfoWcdma extends CellInfo implements Parcelable {
mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
}
+ /** @hide */
+ public CellInfoWcdma(android.hardware.radio.V1_6.CellInfo ci, long timeStamp) {
+ super(ci, timeStamp);
+ final android.hardware.radio.V1_5.CellInfoWcdma ciw = ci.ratSpecificInfo.wcdma();
+ mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+ mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+ }
+
/**
* @return a {@link CellIdentityWcdma} instance.
*/
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 47a8f72a2337..db7d10ae8ce4 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -86,6 +86,15 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
private int mRsrq;
@UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mRssnr;
+ /**
+ * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+ * The definition of CQI in each table is different.
+ *
+ * Reference: 3GPP TS 136.213 section 7.2.3.
+ *
+ * Range [1, 6].
+ */
+ private int mCqiTableIndex;
@UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
private int mCqi;
@UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
@@ -120,24 +129,42 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
* @param rsrp in dBm [-140,-43], UNKNOWN
* @param rsrq in dB [-34, 3], UNKNOWN
* @param rssnr in dB [-20, +30], UNKNOWN
+ * @param cqiTableIndex [1, 6], UNKNOWN
* @param cqi [0, 15], UNKNOWN
* @param timingAdvance [0, 1282], UNKNOWN
*
*/
/** @hide */
- public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi,
- int timingAdvance) {
-
+ public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqiTableIndex,
+ int cqi, int timingAdvance) {
mRssi = inRangeOrUnavailable(rssi, -113, -51);
mSignalStrength = mRssi;
mRsrp = inRangeOrUnavailable(rsrp, -140, -43);
mRsrq = inRangeOrUnavailable(rsrq, -34, 3);
mRssnr = inRangeOrUnavailable(rssnr, -20, 30);
+ mCqiTableIndex = inRangeOrUnavailable(cqiTableIndex, 1, 6);
mCqi = inRangeOrUnavailable(cqi, 0, 15);
mTimingAdvance = inRangeOrUnavailable(timingAdvance, 0, 1282);
updateLevel(null, null);
}
+ /**
+ * Construct a cell signal strength
+ *
+ * @param rssi in dBm [-113,-51], UNKNOWN
+ * @param rsrp in dBm [-140,-43], UNKNOWN
+ * @param rsrq in dB [-34, 3], UNKNOWN
+ * @param rssnr in dB [-20, +30], UNKNOWN
+ * @param cqi [0, 15], UNKNOWN
+ * @param timingAdvance [0, 1282], UNKNOWN
+ *
+ */
+ /** @hide */
+ public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi,
+ int timingAdvance) {
+ this(rssi, rsrp, rsrq, rssnr, CellInfo.UNAVAILABLE, cqi, timingAdvance);
+ }
+
/** @hide */
public CellSignalStrengthLte(android.hardware.radio.V1_0.LteSignalStrength lte) {
// Convert from HAL values as part of construction.
@@ -148,6 +175,16 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
}
/** @hide */
+ public CellSignalStrengthLte(android.hardware.radio.V1_6.LteSignalStrength lte) {
+ // Convert from HAL values as part of construction.
+ this(convertRssiAsuToDBm(lte.base.signalStrength),
+ lte.base.rsrp != CellInfo.UNAVAILABLE ? -lte.base.rsrp : lte.base.rsrp,
+ lte.base.rsrq != CellInfo.UNAVAILABLE ? -lte.base.rsrq : lte.base.rsrq,
+ convertRssnrUnitFromTenDbToDB(lte.base.rssnr), lte.cqiTableIndex, lte.base.cqi,
+ lte.base.timingAdvance);
+ }
+
+ /** @hide */
public CellSignalStrengthLte(CellSignalStrengthLte s) {
copyFrom(s);
}
@@ -159,6 +196,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
mRsrp = s.mRsrp;
mRsrq = s.mRsrq;
mRssnr = s.mRssnr;
+ mCqiTableIndex = s.mCqiTableIndex;
mCqi = s.mCqi;
mTimingAdvance = s.mTimingAdvance;
mLevel = s.mLevel;
@@ -179,6 +217,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
mRsrp = CellInfo.UNAVAILABLE;
mRsrq = CellInfo.UNAVAILABLE;
mRssnr = CellInfo.UNAVAILABLE;
+ mCqiTableIndex = CellInfo.UNAVAILABLE;
mCqi = CellInfo.UNAVAILABLE;
mTimingAdvance = CellInfo.UNAVAILABLE;
mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
@@ -402,6 +441,17 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
}
/**
+ * Get table index for channel quality indicator
+ *
+ * @return the CQI table index if available or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ */
+ /** @hide */
+ public int getCqiTableIndex() {
+ return mCqiTableIndex;
+ }
+
+ /**
* Get channel quality indicator
*
* @return the CQI if available or
@@ -454,7 +504,8 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
@Override
public int hashCode() {
- return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance, mLevel);
+ return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqiTableIndex, mCqi, mTimingAdvance,
+ mLevel);
}
private static final CellSignalStrengthLte sInvalid = new CellSignalStrengthLte();
@@ -476,6 +527,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
&& mRsrp == s.mRsrp
&& mRsrq == s.mRsrq
&& mRssnr == s.mRssnr
+ && mCqiTableIndex == s.mCqiTableIndex
&& mCqi == s.mCqi
&& mTimingAdvance == s.mTimingAdvance
&& mLevel == s.mLevel;
@@ -491,6 +543,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
+ " rsrp=" + mRsrp
+ " rsrq=" + mRsrq
+ " rssnr=" + mRssnr
+ + " cqiTableIndex=" + mCqiTableIndex
+ " cqi=" + mCqi
+ " ta=" + mTimingAdvance
+ " level=" + mLevel
@@ -508,6 +561,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
dest.writeInt(mRsrp);
dest.writeInt(mRsrq);
dest.writeInt(mRssnr);
+ dest.writeInt(mCqiTableIndex);
dest.writeInt(mCqi);
dest.writeInt(mTimingAdvance);
dest.writeInt(mLevel);
@@ -523,6 +577,7 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P
mRsrp = in.readInt();
mRsrq = in.readInt();
mRssnr = in.readInt();
+ mCqiTableIndex = in.readInt();
mCqi = in.readInt();
mTimingAdvance = in.readInt();
mLevel = in.readInt();
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 766019ec382a..1518190bb7f7 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -18,6 +18,7 @@ package android.telephony;
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -27,7 +28,10 @@ import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* 5G NR signal strength related information.
@@ -109,6 +113,28 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
private int mCsiRsrp;
private int mCsiRsrq;
private int mCsiSinr;
+ /**
+ * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+ * The definition of CQI in each table is different.
+ *
+ * Reference: 3GPP TS 138.214 section 5.2.2.1.
+ *
+ * Range [1, 3].
+ */
+ private int mCsiCqiTableIndex;
+ /**
+ * CSI channel quality indicators (CQI) for all subbands.
+ *
+ * If the CQI report is for the entire wideband, a single CQI index is provided.
+ * If the CQI report is for all subbands, one CQI index is provided for each subband,
+ * in ascending order of subband index.
+ * If CQI is not available, the CQI report is empty.
+ *
+ * Reference: 3GPP TS 138.214 section 5.2.2.1.
+ *
+ * Range [0, 15] for each CQI.
+ */
+ private List<Integer> mCsiCqiReport;;
private int mSsRsrp;
private int mSsRsrq;
private int mSsSinr;
@@ -138,16 +164,22 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
* @param csiRsrp CSI reference signal received power.
* @param csiRsrq CSI reference signal received quality.
* @param csiSinr CSI signal-to-noise and interference ratio.
+ * @param csiCqiTableIndex CSI CSI channel quality indicator (CQI) table index.
+ * @param csiCqiReport CSI channel quality indicators (CQI) for all subbands.
* @param ssRsrp SS reference signal received power.
* @param ssRsrq SS reference signal received quality.
* @param ssSinr SS signal-to-noise and interference ratio.
* @hide
*/
- public CellSignalStrengthNr(
- int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) {
+ public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex,
+ List<Integer> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) {
mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44);
mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3);
mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23);
+ mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3);
+ mCsiCqiReport = csiCqiReport.stream()
+ .map(cqi -> new Integer(inRangeOrUnavailable(cqi.intValue(), 1, 3)))
+ .collect(Collectors.toList());
mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44);
mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20);
mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40);
@@ -155,6 +187,21 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
}
/**
+ * @param csiRsrp CSI reference signal received power.
+ * @param csiRsrq CSI reference signal received quality.
+ * @param csiSinr CSI signal-to-noise and interference ratio.
+ * @param ssRsrp SS reference signal received power.
+ * @param ssRsrq SS reference signal received quality.
+ * @param ssSinr SS signal-to-noise and interference ratio.
+ * @hide
+ */
+ public CellSignalStrengthNr(
+ int csiRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr) {
+ this(csiRsrp, csiRsrq, csiSinr, CellInfo.UNAVAILABLE, Collections.emptyList(),
+ ssRsrp, ssRsrq, ssSinr);
+ }
+
+ /**
* @hide
* @param ss signal strength from modem.
*/
@@ -164,6 +211,15 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
}
/**
+ * @hide
+ * @param ss signal strength from modem.
+ */
+ public CellSignalStrengthNr(android.hardware.radio.V1_6.NrSignalStrength ss) {
+ this(flip(ss.base.csiRsrp), flip(ss.base.csiRsrq), ss.base.csiSinr, ss.csiCqiTableIndex,
+ ss.csiCqiReport, flip(ss.base.ssRsrp), flip(ss.base.ssRsrq), ss.base.ssSinr);
+ }
+
+ /**
* Flip sign cell strength value when taking in the value from hal
* @param val cell strength value
* @return flipped value
@@ -232,6 +288,36 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
return mCsiSinr;
}
+ /**
+ * Return CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+ * The definition of CQI in each table is different.
+ *
+ * Reference: 3GPP TS 138.214 section 5.2.2.1.
+ *
+ * Range [1, 3].
+ */
+ /** @hide */
+ public int getCsiCqiTableIndex() {
+ return mCsiCqiTableIndex;
+ }
+ /**
+ * Return a list of CSI channel quality indicators (CQI) for all subbands.
+ *
+ * If the CQI report is for the entire wideband, a single CQI index is provided.
+ * If the CQI report is for all subbands, one CQI index is provided for each subband,
+ * in ascending order of subband index.
+ * If CQI is not available, the CQI report is empty.
+ *
+ * Reference: 3GPP TS 138.214 section 5.2.2.1.
+ *
+ * Range [0, 15] for each CQI.
+ */
+ /** @hide */
+ @NonNull
+ public List<Integer> getCsiCqiReport() {
+ return mCsiCqiReport;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -243,6 +329,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
dest.writeInt(mCsiRsrp);
dest.writeInt(mCsiRsrq);
dest.writeInt(mCsiSinr);
+ dest.writeInt(mCsiCqiTableIndex);
+ dest.writeList(mCsiCqiReport);
dest.writeInt(mSsRsrp);
dest.writeInt(mSsRsrq);
dest.writeInt(mSsSinr);
@@ -253,6 +341,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
mCsiRsrp = in.readInt();
mCsiRsrq = in.readInt();
mCsiSinr = in.readInt();
+ mCsiCqiTableIndex = in.readInt();
+ mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader());
mSsRsrp = in.readInt();
mSsRsrq = in.readInt();
mSsSinr = in.readInt();
@@ -265,6 +355,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
mCsiRsrp = CellInfo.UNAVAILABLE;
mCsiRsrq = CellInfo.UNAVAILABLE;
mCsiSinr = CellInfo.UNAVAILABLE;
+ mCsiCqiTableIndex = CellInfo.UNAVAILABLE;
+ mCsiCqiReport = Collections.emptyList();
mSsRsrp = CellInfo.UNAVAILABLE;
mSsRsrq = CellInfo.UNAVAILABLE;
mSsSinr = CellInfo.UNAVAILABLE;
@@ -408,6 +500,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
mCsiRsrp = s.mCsiRsrp;
mCsiRsrq = s.mCsiRsrq;
mCsiSinr = s.mCsiSinr;
+ mCsiCqiTableIndex = s.mCsiCqiTableIndex;
+ mCsiCqiReport = s.mCsiCqiReport;
mSsRsrp = s.mSsRsrp;
mSsRsrq = s.mSsRsrq;
mSsSinr = s.mSsSinr;
@@ -423,7 +517,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
@Override
public int hashCode() {
- return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mSsRsrp, mSsRsrq, mSsSinr, mLevel);
+ return Objects.hash(mCsiRsrp, mCsiRsrq, mCsiSinr, mCsiCqiTableIndex,
+ mCsiCqiReport, mSsRsrp, mSsRsrq, mSsSinr, mLevel);
}
private static final CellSignalStrengthNr sInvalid = new CellSignalStrengthNr();
@@ -439,6 +534,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
if (obj instanceof CellSignalStrengthNr) {
CellSignalStrengthNr o = (CellSignalStrengthNr) obj;
return mCsiRsrp == o.mCsiRsrp && mCsiRsrq == o.mCsiRsrq && mCsiSinr == o.mCsiSinr
+ && mCsiCqiTableIndex == o.mCsiCqiTableIndex
+ && mCsiCqiReport.equals(o.mCsiCqiReport)
&& mSsRsrp == o.mSsRsrp && mSsRsrq == o.mSsRsrq && mSsSinr == o.mSsSinr
&& mLevel == o.mLevel;
}
@@ -451,7 +548,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
.append(TAG + ":{")
.append(" csiRsrp = " + mCsiRsrp)
.append(" csiRsrq = " + mCsiRsrq)
- .append(" csiSinr = " + mCsiSinr)
+ .append(" csiCqiTableIndex = " + mCsiCqiTableIndex)
+ .append(" csiCqiReport = " + mCsiCqiReport)
.append(" ssRsrp = " + mSsRsrp)
.append(" ssRsrq = " + mSsRsrq)
.append(" ssSinr = " + mSsSinr)
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index b785037e51c1..6571858fc4ae 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -28,8 +28,6 @@ import java.util.Objects;
/**
* Define capability of a modem group. That is, the capabilities
* are shared between those modems defined by list of modem IDs.
- *
- * @hide
*/
public final class PhoneCapability implements Parcelable {
// Hardcoded default DSDS capability.
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 8d49e15da934..95c69ba470c4 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -29,10 +29,6 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Objects;
-/**
- * @hide
- */
-@SystemApi
public final class PhysicalChannelConfig implements Parcelable {
// TODO(b/72993578) consolidate these enums in a central location.
@@ -86,7 +82,7 @@ public final class PhysicalChannelConfig implements Parcelable {
private int mFrequencyRange;
/**
- * The absolute radio frequency channel number, {@link CHANNEL_NUMBER_UNKNOWN} if unknown.
+ * The absolute radio frequency channel number, {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
*/
private int mChannelNumber;
@@ -97,7 +93,7 @@ public final class PhysicalChannelConfig implements Parcelable {
private int[] mContextIds;
/**
- * The physical cell identifier for this cell - PCI, PSC, {@link PHYSICAL_CELL_ID_UNKNOWN}
+ * The physical cell identifier for this cell - PCI, PSC, {@link #PHYSICAL_CELL_ID_UNKNOWN}
* if unknown.
*/
private int mPhysicalCellId;
@@ -153,7 +149,7 @@ public final class PhysicalChannelConfig implements Parcelable {
/**
* @return the absolute radio frequency channel number for this physical channel,
- * {@link CHANNEL_NUMBER_UNKNOWN} if unknown.
+ * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
*/
public int getChannelNumber() {
return mChannelNumber;
@@ -169,7 +165,7 @@ public final class PhysicalChannelConfig implements Parcelable {
* In 5G RAN, this value is physical layer cell identity. The range is [0, 1007].
* Reference: 3GPP TS 38.211 section 7.4.2.1.
*
- * @return the physical cell identifier for this cell, {@link PHYSICAL_CELL_ID_UNKNOWN}
+ * @return the physical cell identifier for this cell, {@link #PHYSICAL_CELL_ID_UNKNOWN}
* if {@link android.telephony.CellInfo#UNAVAILABLE}.
*/
@IntRange(from = 0, to = 1007)
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 7bd0bc0b69ce..b317c5557108 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -187,6 +187,21 @@ public class SignalStrength implements Parcelable {
new CellSignalStrengthNr(signalStrength.nr));
}
+ /**
+ * Constructor for Radio HAL V1.6.
+ *
+ * @param signalStrength signal strength reported from modem.
+ * @hide
+ */
+ public SignalStrength(android.hardware.radio.V1_6.SignalStrength signalStrength) {
+ this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo),
+ new CellSignalStrengthGsm(signalStrength.gsm),
+ new CellSignalStrengthWcdma(signalStrength.wcdma),
+ new CellSignalStrengthTdscdma(signalStrength.tdscdma),
+ new CellSignalStrengthLte(signalStrength.lte),
+ new CellSignalStrengthNr(signalStrength.nr));
+ }
+
private CellSignalStrength getPrimary() {
// This behavior is intended to replicate the legacy behavior of getLevel() by prioritizing
// newer faster RATs for default/for display purposes.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d963970a3d2b..239329cb447e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -124,6 +124,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -155,6 +156,7 @@ import java.util.function.Consumer;
public class TelephonyManager {
private static final String TAG = "TelephonyManager";
+ private TelephonyRegistryManager mTelephonyRegistryMgr;
/**
* To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and
* {@link TelephonyManager#setPreferredOpportunisticDataSubscription}.
@@ -5574,12 +5576,22 @@ public class TelephonyManager {
* @param events The telephony state(s) of interest to the listener,
* as a bitwise-OR combination of {@link PhoneStateListener}
* LISTEN_ flags.
- * @deprecated use {@link #listen(long, PhoneStateListener) instead due to the event number
- * limit increased to 64.
+ * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}.
*/
@Deprecated
public void listen(PhoneStateListener listener, int events) {
- listen(events, listener);
+ boolean notifyNow = getITelephony() != null;
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr != null) {
+ if (events != PhoneStateListener.LISTEN_NONE) {
+ mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId,
+ getOpPackageName(), getAttributionTag(), listener, events, notifyNow);
+ } else {
+ unregisterPhoneStateListener(listener);
+ }
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
}
/**
@@ -5615,18 +5627,21 @@ public class TelephonyManager {
* LISTEN_ flags.
* @param listener The {@link PhoneStateListener} object to register
* (or unregister)
+ * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}.
*/
+ @Deprecated
public void listen(long events, @NonNull PhoneStateListener listener) {
- if (mContext == null) return;
- boolean notifyNow = (getITelephony() != null);
- TelephonyRegistryManager telephonyRegistry =
- (TelephonyRegistryManager)
- mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
- if (telephonyRegistry != null) {
- telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(),
- listener, events, notifyNow);
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr != null) {
+ if (events != PhoneStateListener.LISTEN_NONE) {
+ mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId,
+ getOpPackageName(), getAttributionTag(), listener,
+ Long.valueOf(events).intValue(), getITelephony() != null);
+ } else {
+ unregisterPhoneStateListener(listener);
+ }
} else {
- Rlog.w(TAG, "telephony registry not ready.");
+ throw new IllegalStateException("telephony service is null.");
}
}
@@ -11694,18 +11709,6 @@ public class TelephonyManager {
}
/**
- * In this mode, modem will not send specified indications when screen is off.
- * @hide
- */
- public static final int INDICATION_UPDATE_MODE_NORMAL = 1;
-
- /**
- * In this mode, modem will still send specified indications when screen is off.
- * @hide
- */
- public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF = 2;
-
- /**
* The indication for signal strength update.
* @hide
*/
@@ -12302,23 +12305,15 @@ public class TelephonyManager {
@NonNull
public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
@EmergencyServiceCategories int categories) {
- Map<Integer, List<EmergencyNumber>> emergencyNumberList = new HashMap<>();
+ Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- emergencyNumberList = telephony.getEmergencyNumberList(
- mContext.getOpPackageName(), mContext.getAttributionTag());
- if (emergencyNumberList != null) {
- for (Integer subscriptionId : emergencyNumberList.keySet()) {
- List<EmergencyNumber> numberList = emergencyNumberList.get(subscriptionId);
- for (EmergencyNumber number : numberList) {
- if (!number.isInEmergencyServiceCategories(categories)) {
- numberList.remove(number);
- }
- }
- }
- }
- return emergencyNumberList;
+ Map<Integer, List<EmergencyNumber>> emergencyNumberList =
+ telephony.getEmergencyNumberList(mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ emergencyNumberListForCategories =
+ filterEmergencyNumbersByCategories(emergencyNumberList, categories);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -12326,7 +12321,34 @@ public class TelephonyManager {
Log.e(TAG, "getEmergencyNumberList with Categories RemoteException", ex);
ex.rethrowAsRuntimeException();
}
- return emergencyNumberList;
+ return emergencyNumberListForCategories;
+ }
+
+ /**
+ * Filter emergency numbers with categories.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
+ Map<Integer, List<EmergencyNumber>> emergencyNumberList,
+ @EmergencyServiceCategories int categories) {
+ Map<Integer, List<EmergencyNumber>> emergencyNumberListForCategories = new HashMap<>();
+ if (emergencyNumberList != null) {
+ for (Integer subscriptionId : emergencyNumberList.keySet()) {
+ List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
+ subscriptionId);
+ List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
+ for (EmergencyNumber number : allNumbersForSub) {
+ if (number.isInEmergencyServiceCategories(categories)) {
+ numbersForCategoriesPerSub.add(number);
+ }
+ }
+ emergencyNumberListForCategories.put(
+ subscriptionId, numbersForCategoriesPerSub);
+ }
+ }
+ return emergencyNumberListForCategories;
}
/**
@@ -14328,4 +14350,69 @@ public class TelephonyManager {
}
return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
}
+ /**
+ * Registers a listener object to receive notification of changes
+ * in specified telephony states.
+ * <p>
+ * To register a listener, pass a {@link PhoneStateListener} which implements
+ * interfaces of events. For example,
+ * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements
+ * {@link PhoneStateListener.ServiceStateChangedListener}.
+ *
+ * At registration, and when a specified telephony state changes, the telephony manager invokes
+ * the appropriate callback method on the listener object and passes the current (updated)
+ * values.
+ * <p>
+ *
+ * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+ * applies to the given subId. Otherwise, applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
+ * pass a separate listener object to each TelephonyManager object created with
+ * {@link #createForSubscriptionId}.
+ *
+ * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+ * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+ * {@link SecurityException} will be thrown otherwise.
+ *
+ * This API should be used sparingly -- large numbers of listeners will cause system
+ * instability. If a process has registered too many listeners without unregistering them, it
+ * may encounter an {@link IllegalStateException} when trying to register more listeners.
+ *
+ * @param executor The executor of where the callback will execute.
+ * @param listener The {@link PhoneStateListener} object to register.
+ */
+ public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull PhoneStateListener listener) {
+ if (executor == null || listener == null) {
+ throw new IllegalArgumentException("PhoneStateListener and executor must be non-null");
+ }
+ mTelephonyRegistryMgr = (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (mTelephonyRegistryMgr != null) {
+ mTelephonyRegistryMgr.registerPhoneStateListener(executor, mSubId,
+ getOpPackageName(), getAttributionTag(), listener, getITelephony() != null);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ }
+
+ /**
+ * Unregister an existing {@link PhoneStateListener}.
+ *
+ * @param listener The {@link PhoneStateListener} object to unregister.
+ */
+ public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) {
+
+ if (mContext == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr != null) {
+ mTelephonyRegistryMgr.unregisterPhoneStateListener(mSubId, getOpPackageName(),
+ getAttributionTag(), listener, getITelephony() != null);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index e8234178be9d..9a3f592480d1 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -231,8 +231,9 @@ public class MmTelFeature extends ImsFeature {
* The capabilities that are used in MmTelFeature are defined as
* {@link MmTelCapabilities#CAPABILITY_TYPE_VOICE},
* {@link MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
- * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
- * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
+ * {@link MmTelCapabilities#CAPABILITY_TYPE_UT},
+ * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}, and
+ * {@link MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER}.
*
* The capabilities of this MmTelFeature will be set by the framework.
*/
@@ -275,7 +276,8 @@ public class MmTelFeature extends ImsFeature {
CAPABILITY_TYPE_VOICE,
CAPABILITY_TYPE_VIDEO,
CAPABILITY_TYPE_UT,
- CAPABILITY_TYPE_SMS
+ CAPABILITY_TYPE_SMS,
+ CAPABILITY_TYPE_CALL_COMPOSER
})
@Retention(RetentionPolicy.SOURCE)
public @interface MmTelCapability {}
@@ -301,6 +303,11 @@ public class MmTelFeature extends ImsFeature {
public static final int CAPABILITY_TYPE_SMS = 1 << 3;
/**
+ * This MmTelFeature supports Call Composer (section 2.4 of RC.20)
+ */
+ public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
+
+ /**
* @hide
*/
@Override
@@ -343,6 +350,8 @@ public class MmTelFeature extends ImsFeature {
builder.append(isCapable(CAPABILITY_TYPE_UT));
builder.append(" SMS: ");
builder.append(isCapable(CAPABILITY_TYPE_SMS));
+ builder.append(" CALL_COMPOSER: ");
+ builder.append(isCapable(CAPABILITY_TYPE_CALL_COMPOSER));
builder.append("]");
return builder.toString();
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index e0290a5dc2af..e757d9f70ccc 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -126,7 +126,7 @@ public class ImsConfigImplBase {
*/
@Override
public synchronized String getConfigString(int item) throws RemoteException {
- if (mProvisionedIntValue.containsKey(item)) {
+ if (mProvisionedStringValue.containsKey(item)) {
return mProvisionedStringValue.get(item);
} else {
String retVal = getImsConfigImpl().getConfigString(item);
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
new file mode 100644
index 000000000000..54d70478f762
--- /dev/null
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
+import android.os.UidBatteryConsumer;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BatteryUsageStatsPerfTest {
+
+ @Rule
+ public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * Measures the performance of {@link BatteryStatsManager#getBatteryUsageStats()},
+ * which triggers a battery stats sync on every iteration.
+ */
+ @Test
+ public void testGetBatteryUsageStats() {
+ final Context context = InstrumentationRegistry.getContext();
+ final BatteryStatsManager batteryStatsManager =
+ context.getSystemService(BatteryStatsManager.class);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats();
+
+ state.pauseTiming();
+
+ List<UidBatteryConsumer> uidBatteryConsumers =
+ batteryUsageStats.getUidBatteryConsumers();
+ double power = 0;
+ for (int i = 0; i < uidBatteryConsumers.size(); i++) {
+ UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(i);
+ power += uidBatteryConsumer.getConsumedPower();
+ }
+
+ assertThat(power).isGreaterThan(0.0);
+
+ state.resumeTiming();
+ }
+ }
+}
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index 58f56f29528c..f9f9d5828171 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -21,6 +21,8 @@
<!-- Read and write traces from external storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<!-- Write secure settings -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<!-- Capture screen contents -->
@@ -34,7 +36,8 @@
<!-- Workaround grant runtime permission exception from b/152733071 -->
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
- <application>
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <application android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
</application>
diff --git a/tests/FlickerTests/AndroidTestPhysicalDevices.xml b/tests/FlickerTests/AndroidTestPhysicalDevices.xml
index 16504389098c..abd620f5c157 100644
--- a/tests/FlickerTests/AndroidTestPhysicalDevices.xml
+++ b/tests/FlickerTests/AndroidTestPhysicalDevices.xml
@@ -34,7 +34,7 @@
<option name="hidden-api-checks" value="false" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.server.wm.flicker/files" />
+ <option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
</metrics_collector>
diff --git a/tests/FlickerTests/AndroidTestVirtualDevices.xml b/tests/FlickerTests/AndroidTestVirtualDevices.xml
index 222212a74cd0..9a5413ae3ad2 100644
--- a/tests/FlickerTests/AndroidTestVirtualDevices.xml
+++ b/tests/FlickerTests/AndroidTestVirtualDevices.xml
@@ -34,7 +34,7 @@
<option name="hidden-api-checks" value="false" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.server.wm.flicker/files" />
+ <option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index a85e92c92093..bb03237ca4c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -239,7 +239,7 @@ fun EventLogAssertion.focusChanges(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all(enabled = enabled, bugId = bugId) {
+ all("focusChanges", bugId, enabled) {
this.focusChanges(windows)
}
}
@@ -248,7 +248,7 @@ fun EventLogAssertion.focusDoesNotChange(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all(enabled = enabled, bugId = bugId) {
+ all("focusDoesNotChange", bugId, enabled) {
this.focusDoesNotChange()
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 0c584f4973e8..5aef314e0caf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import androidx.test.filters.FlakyTest
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -53,6 +54,7 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 174658929)
class OpenAppFromOverviewTest(
testName: String,
flickerSpec: Flicker
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 0db064ab79c5..9d4a71874f67 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -67,7 +67,7 @@ class ChangeAppRotationTest(
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testApp = StandardAppHelper(instrumentation,
"com.android.server.wm.flicker.testapp", "SimpleApp")
- return FlickerTestRunnerFactory(instrumentation)
+ return FlickerTestRunnerFactory(instrumentation, repetitions = 10)
.buildRotationTest { configuration ->
withTestName {
buildTestTag(
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index a8aab2a899fb..a72b07c45dd8 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -6,6 +6,7 @@ android_test {
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
+ "truth-prebuilt",
"ub-uiautomator",
],
test_suites: ["device-tests"],
diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
new file mode 100644
index 000000000000..f919a3eaf271
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 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.test.input
+
+import android.graphics.FrameInfo
+import android.os.SystemClock
+import android.view.ViewFrameInfo
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+class ViewFrameInfoTest {
+ companion object {
+ private const val TAG = "ViewFrameInfoTest"
+ }
+ private val mViewFrameInfo = ViewFrameInfo()
+ private var mTimeStarted: Long = 0
+
+ @Before
+ fun setUp() {
+ mViewFrameInfo.reset()
+ mViewFrameInfo.updateOldestInputEvent(10)
+ mViewFrameInfo.updateNewestInputEvent(20)
+ mViewFrameInfo.flags = mViewFrameInfo.flags or FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED
+ mTimeStarted = SystemClock.uptimeNanos()
+ mViewFrameInfo.markDrawStart()
+ }
+
+ @Test
+ fun testPopulateFields() {
+ assertThat(mViewFrameInfo.drawStart).isGreaterThan(mTimeStarted)
+ assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(10)
+ assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(20)
+ assertThat(mViewFrameInfo.flags).isEqualTo(FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
+ }
+
+ @Test
+ fun testReset() {
+ mViewFrameInfo.reset()
+ // Ensure that the original object is reset correctly
+ assertThat(mViewFrameInfo.drawStart).isEqualTo(0)
+ assertThat(mViewFrameInfo.oldestInputEventTime).isEqualTo(0)
+ assertThat(mViewFrameInfo.newestInputEventTime).isEqualTo(0)
+ assertThat(mViewFrameInfo.flags).isEqualTo(0)
+ }
+
+ @Test
+ fun testUpdateFrameInfoFromViewFrameInfo() {
+ val frameInfo = FrameInfo()
+ // By default, all values should be zero
+ assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0)
+ assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0)
+ assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0)
+ assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0)
+
+ // The values inside FrameInfo should match those from ViewFrameInfo after we update them
+ mViewFrameInfo.populateFrameInfo(frameInfo)
+ assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10)
+ assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20)
+ assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(
+ FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED)
+ assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted)
+ }
+} \ No newline at end of file
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 9738e58543e1..104758de49f1 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -22,6 +22,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -43,10 +44,15 @@ import android.os.SystemProperties;
import android.os.test.TestLooper;
import android.provider.DeviceConfig;
import android.util.AtomicFile;
+import android.util.LongArrayQueue;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
import androidx.test.InstrumentationRegistry;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.XmlUtils;
import com.android.server.PackageWatchdog.HealthCheckState;
import com.android.server.PackageWatchdog.MonitoredPackage;
import com.android.server.PackageWatchdog.PackageHealthObserver;
@@ -64,6 +70,7 @@ import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.io.File;
+import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -739,7 +746,8 @@ public class PackageWatchdogTest {
false /* hasPassedHealthCheck */);
MonitoredPackage m2 = wd.newMonitoredPackage(APP_B, LONG_DURATION, false);
MonitoredPackage m3 = wd.newMonitoredPackage(APP_C, LONG_DURATION, false);
- MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true);
+ MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true,
+ new LongArrayQueue());
// Verify transition: inactive -> active -> passed
// Verify initially inactive
@@ -1210,6 +1218,73 @@ public class PackageWatchdogTest {
assertThat(observer.mMitigationCounts).isEqualTo(List.of(1, 2, 3, 3, 2, 3));
}
+ @Test
+ public void testNormalizingMitigationCalls() {
+ PackageWatchdog watchdog = createWatchdog();
+
+ LongArrayQueue mitigationCalls = new LongArrayQueue();
+ mitigationCalls.addLast(1000);
+ mitigationCalls.addLast(2000);
+ mitigationCalls.addLast(3000);
+
+ MonitoredPackage pkg = watchdog.newMonitoredPackage(
+ "test", 123, 456, true, mitigationCalls);
+
+ // Make current system uptime 10000ms.
+ moveTimeForwardAndDispatch(9999);
+
+ LongArrayQueue expectedCalls = pkg.normalizeMitigationCalls();
+
+ assertThat(expectedCalls.size()).isEqualTo(mitigationCalls.size());
+
+ for (int i = 0; i < mitigationCalls.size(); i++) {
+ assertThat(expectedCalls.get(i)).isEqualTo(mitigationCalls.get(i) - 10000);
+ }
+ }
+
+ /**
+ * Ensure that a {@link MonitoredPackage} may be correctly written and read in order to persist
+ * across reboots.
+ */
+ @Test
+ public void testWritingAndReadingMonitoredPackage() throws Exception {
+ PackageWatchdog watchdog = createWatchdog();
+
+ LongArrayQueue mitigationCalls = new LongArrayQueue();
+ mitigationCalls.addLast(1000);
+ mitigationCalls.addLast(2000);
+ mitigationCalls.addLast(3000);
+ MonitoredPackage writePkg = watchdog.newMonitoredPackage(
+ "test.package", 1000, 2000, true, mitigationCalls);
+
+ // Move time forward so that the current uptime is 4000ms. Therefore, the written mitigation
+ // calls will each be reduced by 4000.
+ moveTimeForwardAndDispatch(3999);
+ LongArrayQueue expectedCalls = new LongArrayQueue();
+ expectedCalls.addLast(-3000);
+ expectedCalls.addLast(-2000);
+ expectedCalls.addLast(-1000);
+ MonitoredPackage expectedPkg = watchdog.newMonitoredPackage(
+ "test.package", 1000, 2000, true, expectedCalls);
+
+ // Write the package
+ File tmpFile = File.createTempFile("package-watchdog-test", ".xml");
+ AtomicFile testFile = new AtomicFile(tmpFile);
+ FileOutputStream stream = testFile.startWrite();
+ TypedXmlSerializer outputSerializer = Xml.resolveSerializer(stream);
+ outputSerializer.startDocument(null, true);
+ writePkg.writeLocked(outputSerializer);
+ outputSerializer.endDocument();
+ testFile.finishWrite(stream);
+
+ // Read the package
+ TypedXmlPullParser parser = Xml.resolvePullParser(testFile.openRead());
+ XmlUtils.beginDocument(parser, "package");
+ MonitoredPackage readPkg = watchdog.parseMonitoredPackage(parser);
+
+ assertTrue(readPkg.isEqualTo(expectedPkg));
+ }
+
private void adoptShellPermissions(String... permissions) {
InstrumentationRegistry
.getInstrumentation()
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
index 0f4c4603f9b4..7f9f2dcf2bde 100644
--- a/tests/RollbackTest/TEST_MAPPING
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "RollbackTest"
},
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index f55d4d474bfb..3a40696d1add 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -31,7 +31,6 @@ java_test_host {
"testng",
"compatibility-tradefed",
"frameworks-base-hostutils",
- "module_test_util",
"cts-install-lib-host",
],
data: [
diff --git a/tests/StagedInstallTest/TEST_MAPPING b/tests/StagedInstallTest/TEST_MAPPING
index 5a7a5a766b88..fa2a60b21b50 100644
--- a/tests/StagedInstallTest/TEST_MAPPING
+++ b/tests/StagedInstallTest/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "StagedInstallInternalTest"
}
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 27ccbc78cf96..9e1ea2e04528 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -29,7 +29,6 @@ import android.cts.install.lib.host.InstallUtilsHost;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.tests.rollback.host.AbandonSessionsRule;
-import com.android.tests.util.ModuleTestUtils;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -60,7 +59,6 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
private static final String APK_A = "TestAppAv1.apk";
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
- private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this);
private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
/**
@@ -161,7 +159,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
assumeTrue("Device does not support updating APEX",
mHostUtils.isApexUpdateSupported());
- final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+ final File apexFile = mHostUtils.getTestFile(SHIM_V2);
final String output = getDevice().executeAdbCommand("install", "--staged",
"--staged-ready-timeout", "60000", apexFile.getAbsolutePath());
assertThat(output).contains("Reboot device to apply staged session");
@@ -176,7 +174,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
assumeTrue("Device does not support updating APEX",
mHostUtils.isApexUpdateSupported());
- final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+ final File apexFile = mHostUtils.getTestFile(SHIM_V2);
final String output = getDevice().executeAdbCommand("install", "--staged",
apexFile.getAbsolutePath());
assertThat(output).contains("Reboot device to apply staged session");
@@ -191,7 +189,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
assumeTrue("Device does not support updating APEX",
mHostUtils.isApexUpdateSupported());
- final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+ final File apexFile = mHostUtils.getTestFile(SHIM_V2);
final String output = getDevice().executeAdbCommand("install", "--staged",
"--staged-ready-timeout", "0", apexFile.getAbsolutePath());
assertThat(output).doesNotContain("Reboot device to apply staged session");
@@ -207,7 +205,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
assumeTrue("Device does not support updating APEX",
mHostUtils.isApexUpdateSupported());
- final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+ final File apexFile = mHostUtils.getTestFile(SHIM_V2);
String output = getDevice().executeAdbCommand("install", "--staged",
"--enable-rollback", apexFile.getAbsolutePath());
assertThat(output).contains("Reboot device to apply staged session");
@@ -224,8 +222,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
assumeTrue("Device does not support updating APEX",
mHostUtils.isApexUpdateSupported());
- final File apexFile = mTestUtils.getTestFile(SHIM_V2);
- final File apkFile = mTestUtils.getTestFile(APK_A);
+ final File apexFile = mHostUtils.getTestFile(SHIM_V2);
+ final File apkFile = mHostUtils.getTestFile(APK_A);
final String output = getDevice().executeAdbCommand("install-multi-package",
apexFile.getAbsolutePath(), apkFile.getAbsolutePath());
assertThat(output).contains("Created parent session");
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
index cd4b38516bc3..273833b91950 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -23,7 +23,7 @@ import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.android.server.wm.flicker.monitor.LayersTraceMonitor
import com.android.server.wm.flicker.monitor.withSFTracing
-import com.android.server.wm.flicker.traces.layers.LayersTrace
+import com.android.server.wm.traces.parser.layers.LayersTrace
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -52,7 +52,8 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
}
fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace {
- return withSFTracing(instrumentation, TRACE_FLAGS) {
+ return withSFTracing(TRACE_FLAGS,
+ outputDir = instrumentation.targetContext.dataDir.toPath()) {
scenarioRule.getScenario().onActivity {
predicate(it)
}
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
index fe9deae80407..2e3467aff9ba 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
@@ -68,7 +68,8 @@ class ResizeTasksSyncTest {
val firstBounds = Rect(0, 0, 1080, 800)
val secondBounds = Rect(0, 1000, 1080, 1800)
- val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
+ val trace = withSFTracing(TRACE_FLAGS,
+ outputDir = instrumentation.targetContext.dataDir.toPath()) {
lateinit var resizeReadyLatch: CountDownLatch
scenarioRule.getScenario().onActivity {
resizeReadyLatch = it.resizeTaskView(firstBounds, secondBounds)
@@ -77,17 +78,13 @@ class ResizeTasksSyncTest {
}
// find the frame which match resized buffer size.
- var frame: Long = -1
- loop@ for (trace in trace.entries) {
- for (layer in trace.flattenedLayers) {
- if (layer.proto.activeBuffer != null &&
- layer.proto.activeBuffer.width == firstBounds.width() &&
- layer.proto.activeBuffer.height == firstBounds.height()) {
- frame = layer.proto.currFrame
- break@loop
- }
- }
- }
+ val frame = trace.entries.flatMap { it.flattenedLayers }
+ .firstOrNull { layer ->
+ !layer.isActiveBufferEmpty &&
+ layer.activeBuffer?.width == firstBounds.width() &&
+ layer.activeBuffer?.height == firstBounds.height()
+ }?.currFrame ?: -1
+
assertNotEquals(-1, frame)
// layer bounds should be related to parent surfaceview.
secondBounds.offsetTo(0, 0)
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index dd3f5bebdb8e..77e9f12c7152 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -33,6 +33,9 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verifyNoMoreInteractions
import java.util.UUID
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
@@ -87,8 +90,8 @@ class NetworkProviderTest {
) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
}
- private fun createNetworkProvider(): TestNetworkProvider {
- return TestNetworkProvider(context, mHandlerThread.looper)
+ private fun createNetworkProvider(ctx: Context = context): TestNetworkProvider {
+ return TestNetworkProvider(ctx, mHandlerThread.looper)
}
@Test
@@ -169,7 +172,12 @@ class NetworkProviderTest {
@Test
fun testDeclareNetworkRequestUnfulfillable() {
- val provider = createNetworkProvider()
+ val mockContext = mock(Context::class.java)
+ val provider = createNetworkProvider(mockContext)
+ // ConnectivityManager not required at creation time
+ verifyNoMoreInteractions(mockContext)
+ doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE)
+
mCm.registerNetworkProvider(provider)
val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index 968b3071bf1d..ea763d2e931e 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -16,6 +16,12 @@
package com.android.server;
+import static android.util.DebugUtils.valueToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -29,15 +35,19 @@ import android.content.Context;
import android.net.INetd;
import android.net.INetdUnsolicitedEventListener;
import android.net.LinkAddress;
+import android.net.NetworkPolicyManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
-import com.android.server.NetworkManagementService.SystemServices;
+import com.android.server.NetworkManagementService.Dependencies;
import com.android.server.net.BaseNetworkObserver;
import org.junit.After;
@@ -49,13 +59,14 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.function.BiFunction;
+
/**
* Tests for {@link NetworkManagementService}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkManagementServiceTest {
-
private NetworkManagementService mNMService;
@Mock private Context mContext;
@@ -66,7 +77,9 @@ public class NetworkManagementServiceTest {
@Captor
private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor;
- private final SystemServices mServices = new SystemServices() {
+ private final MockDependencies mDeps = new MockDependencies();
+
+ private final class MockDependencies extends Dependencies {
@Override
public IBinder getService(String name) {
switch (name) {
@@ -76,14 +89,21 @@ public class NetworkManagementServiceTest {
throw new UnsupportedOperationException("Unknown service " + name);
}
}
+
@Override
public void registerLocalService(NetworkManagementInternal nmi) {
}
+
@Override
public INetd getNetd() {
return mNetdService;
}
- };
+
+ @Override
+ public int getCallingUid() {
+ return Process.SYSTEM_UID;
+ }
+ }
@Before
public void setUp() throws Exception {
@@ -91,7 +111,7 @@ public class NetworkManagementServiceTest {
doNothing().when(mNetdService)
.registerUnsolicitedEventListener(mUnsolListenerCaptor.capture());
// Start the service and wait until it connects to our socket.
- mNMService = NetworkManagementService.create(mContext, mServices);
+ mNMService = NetworkManagementService.create(mContext, mDeps);
}
@After
@@ -192,4 +212,105 @@ public class NetworkManagementServiceTest {
// Make sure nothing else was called.
verifyNoMoreInteractions(observer);
}
+
+ @Test
+ public void testFirewallEnabled() {
+ mNMService.setFirewallEnabled(true);
+ assertTrue(mNMService.isFirewallEnabled());
+
+ mNMService.setFirewallEnabled(false);
+ assertFalse(mNMService.isFirewallEnabled());
+ }
+
+ private static final int TEST_UID = 111;
+
+ @Test
+ public void testNetworkRestrictedDefault() {
+ assertFalse(mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ public void testMeteredNetworkRestrictions() throws RemoteException {
+ // Make sure the mocked netd method returns true.
+ doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean());
+
+ // Restrict usage of mobile data in background
+ mNMService.setUidMeteredNetworkDenylist(TEST_UID, true);
+ assertTrue("Should be true since mobile data usage is restricted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setDataSaverModeEnabled(true);
+ verify(mNetdService).bandwidthEnableDataSaver(true);
+
+ mNMService.setUidMeteredNetworkDenylist(TEST_UID, false);
+ assertTrue("Should be true since data saver is on and the uid is not allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setUidMeteredNetworkAllowlist(TEST_UID, true);
+ assertFalse("Should be false since data saver is on and the uid is allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ // remove uid from allowlist and turn datasaver off again
+ mNMService.setUidMeteredNetworkAllowlist(TEST_UID, false);
+ mNMService.setDataSaverModeEnabled(false);
+ verify(mNetdService).bandwidthEnableDataSaver(false);
+ assertFalse("Network should not be restricted when data saver is off",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ public void testFirewallChains() {
+ final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>();
+ // Dozable chain
+ final ArrayMap<Integer, Boolean> isRestrictedForDozable = new ArrayMap<>();
+ isRestrictedForDozable.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
+ isRestrictedForDozable.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForDozable.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable);
+ // Powersaver chain
+ final ArrayMap<Integer, Boolean> isRestrictedForPowerSave = new ArrayMap<>();
+ isRestrictedForPowerSave.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
+ isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave);
+ // Standby chain
+ final ArrayMap<Integer, Boolean> isRestrictedForStandby = new ArrayMap<>();
+ isRestrictedForStandby.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, false);
+ isRestrictedForStandby.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForStandby.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_STANDBY, isRestrictedForStandby);
+ // Restricted mode chain
+ final ArrayMap<Integer, Boolean> isRestrictedForRestrictedMode = new ArrayMap<>();
+ isRestrictedForRestrictedMode.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
+ isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_RESTRICTED, isRestrictedForRestrictedMode);
+
+ final int[] chains = {
+ INetd.FIREWALL_CHAIN_STANDBY,
+ INetd.FIREWALL_CHAIN_POWERSAVE,
+ INetd.FIREWALL_CHAIN_DOZABLE,
+ INetd.FIREWALL_CHAIN_RESTRICTED
+ };
+ final int[] states = {
+ INetd.FIREWALL_RULE_ALLOW,
+ INetd.FIREWALL_RULE_DENY,
+ NetworkPolicyManager.FIREWALL_RULE_DEFAULT
+ };
+ BiFunction<Integer, Integer, String> errorMsg = (chain, state) -> {
+ return String.format("Unexpected value for chain: %s and state: %s",
+ valueToString(INetd.class, "FIREWALL_CHAIN_", chain),
+ valueToString(INetd.class, "FIREWALL_RULE_", state));
+ };
+ for (int chain : chains) {
+ final ArrayMap<Integer, Boolean> expectedValues = expected.get(chain);
+ mNMService.setFirewallChainEnabled(chain, true);
+ for (int state : states) {
+ mNMService.setFirewallUidRule(chain, TEST_UID, state);
+ assertEquals(errorMsg.apply(chain, state),
+ expectedValues.get(state), mNMService.isNetworkRestricted(TEST_UID));
+ }
+ mNMService.setFirewallChainEnabled(chain, false);
+ }
+ }
}
diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING
index 8c515109a309..7ddc30872cb9 100644
--- a/wifi/TEST_MAPPING
+++ b/wifi/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "CtsWifiTestCases",
"options": [
@@ -11,9 +11,6 @@
],
"mainline-presubmit": [
{
- "name": "FrameworksWifiApiTests[com.google.android.wifi.apex]"
- },
- {
"name": "CtsWifiTestCases[com.google.android.wifi.apex]",
"options": [
{
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index ce2b8ca4f2cd..e11b33efcc33 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -332,6 +332,7 @@ package android.net.wifi {
method public boolean is5GHzBandSupported();
method public boolean is6GHzBandSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled();
+ method public boolean isBridgedApConcurrencySupported();
method @Deprecated public boolean isDeviceToApRttSupported();
method public boolean isEasyConnectSupported();
method public boolean isEnhancedOpenSupported();
@@ -342,6 +343,7 @@ package android.net.wifi {
method @Deprecated public boolean isScanAlwaysAvailable();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled();
method public boolean isStaApConcurrencySupported();
+ method public boolean isStaBridgedApConcurrencySupported();
method public boolean isTdlsSupported();
method public boolean isWapiSupported();
method public boolean isWifiEnabled();
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index 48d9fd453b05..eba744330a2e 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -483,6 +483,7 @@ package android.net.wifi {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>);
method public boolean is60GHzBandSupported();
method public boolean isApMacRandomizationSupported();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean isCarrierNetworkOffloadEnabled(int, boolean);
method public boolean isConnectedMacRandomizationSupported();
method @Deprecated public boolean isDeviceToDeviceRttSupported();
method public boolean isPortableHotspotSupported();
@@ -501,6 +502,7 @@ package android.net.wifi {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setCarrierNetworkOffloadEnabled(int, boolean, boolean);
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) public void setCoexUnsafeChannels(@NonNull java.util.Set<android.net.wifi.CoexUnsafeChannel>, int);
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 6dee751f5894..866e913800fd 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -296,4 +296,8 @@ interface IWifiManager
void startTemporarilyDisablingAllNonCarrierMergedWifi(int subId);
void stopTemporarilyDisablingAllNonCarrierMergedWifi();
+
+ void setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged, boolean enabled);
+
+ boolean isCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged);
}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index fddc8899a0c8..226d1a368223 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -935,6 +935,9 @@ public final class SoftApConfiguration implements Parcelable {
* on the requested bands (if possible).
* <p>
*
+ * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine
+ * whether or not concurrent APs are supported.
+ *
* @param bands Array of the {@link #BandType}.
* @return Builder for chaining.
* @throws IllegalArgumentException when more than 2 bands are set or an invalid band type
@@ -1007,6 +1010,9 @@ public final class SoftApConfiguration implements Parcelable {
* The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain
* valid channels in each band.
*
+ * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine
+ * whether or not concurrent APs are supported.
+ *
* <p>
* If not set, the default for the channel is the special value 0 which has the framework
* auto-select a valid channel from the band configured with {@link #setBands(int[])}.
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 90edc4523b7b..e127ea99c716 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -1335,6 +1335,16 @@ public class WifiEnterpriseConfig implements Parcelable {
}
/**
+ * Initialize the value of the app installed device key and cert flag.
+ *
+ * @param isAppInstalledDeviceKeyAndCert true or false
+ * @hide
+ */
+ public void initIsAppInstalledDeviceKeyAndCert(boolean isAppInstalledDeviceKeyAndCert) {
+ mIsAppInstalledDeviceKeyAndCert = isAppInstalledDeviceKeyAndCert;
+ }
+
+ /**
* Check if CA certificate was installed by an app, or manually (not by an app). If true,
* CA certificate will be removed from key storage when this network is removed. If not,
* then certificates and keys remain persistent until the user manually removes them.
@@ -1348,6 +1358,16 @@ public class WifiEnterpriseConfig implements Parcelable {
}
/**
+ * Initialize the value of the app installed root CA cert flag.
+ *
+ * @param isAppInstalledCaCert true or false
+ * @hide
+ */
+ public void initIsAppInstalledCaCert(boolean isAppInstalledCaCert) {
+ mIsAppInstalledCaCert = isAppInstalledCaCert;
+ }
+
+ /**
* Set the OCSP type.
* @param ocsp is one of {@link ##OCSP_NONE}, {@link #OCSP_REQUEST_CERT_STATUS},
* {@link #OCSP_REQUIRE_CERT_STATUS} or
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b8fa1e18ed28..2b931a380f43 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -53,6 +53,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.os.WorkSource;
import android.os.connectivity.WifiActivityEnergyInfo;
+import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.CloseGuard;
@@ -2488,6 +2489,12 @@ public class WifiManager {
/** @hide */
public static final long WIFI_FEATURE_SAE_PK = 0x10000000000L; // SAE-PK
+ /** @hide */
+ public static final long WIFI_FEATURE_STA_BRIDGED_AP = 0x20000000000L; // STA + Bridged AP
+
+ /** @hide */
+ public static final long WIFI_FEATURE_BRIDGED_AP = 0x40000000000L; // Bridged AP
+
private long getSupportedFeatures() {
try {
return mService.getSupportedFeatures();
@@ -2688,6 +2695,40 @@ public class WifiManager {
}
/**
+ * Query whether the device supports Station (STA) + Bridged access point (AP)
+ * concurrency or not.
+ *
+ * The bridged AP support means that the device supports AP + AP concurrency with the 2 APs
+ * bridged together.
+ *
+ * See {@link SoftApConfiguration.Builder#setBands(int[])}
+ * or {@link SoftApConfiguration.Builder#setChannels(SparseIntArray)} to configure bridged AP
+ * when the bridged AP supported.
+ *
+ * @return true if this device supports STA + bridged AP concurrency, false otherwise.
+ */
+ public boolean isStaBridgedApConcurrencySupported() {
+ return isFeatureSupported(WIFI_FEATURE_STA_BRIDGED_AP);
+ }
+
+ /**
+ * Query whether the device supports Bridged Access point (AP) concurrency or not.
+ *
+ * The bridged AP support means that the device supports AP + AP concurrency with the 2 APs
+ * bridged together.
+ *
+ * See {@link SoftApConfiguration.Builder#setBands(int[])}
+ * or {@link SoftApConfiguration.Builder#setChannels(SparseIntArray)} to configure bridged AP
+ * when the bridged AP supported.
+ *
+ * @return true if this device supports bridged AP concurrency, false otherwise.
+ */
+ public boolean isBridgedApConcurrencySupported() {
+ return isFeatureSupported(WIFI_FEATURE_BRIDGED_AP);
+ }
+
+
+ /**
* Interface for Wi-Fi activity energy info listener. Should be implemented by applications and
* set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}.
*
@@ -6721,4 +6762,63 @@ public class WifiManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Sets the state of carrier offload on merged or unmerged networks for specified subscription.
+ *
+ * <p>
+ * When a subscription's carrier network offload is disabled, all network suggestions related to
+ * this subscription will not be considered for auto join.
+ * <p>
+ * If calling app want disable all carrier network offload from a specified subscription, should
+ * call this API twice to disable both merged and unmerged carrier network suggestions.
+ *
+ * @param subscriptionId See {@link SubscriptionInfo#getSubscriptionId()}.
+ * @param merged True for carrier merged network, false otherwise.
+ * See {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)}
+ * @param enabled True for enable carrier network offload, false otherwise.
+ * @see #isCarrierNetworkOffloadEnabled(int, boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public void setCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged,
+ boolean enabled) {
+ if (!SdkLevel.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ mService.setCarrierNetworkOffloadEnabled(subscriptionId, merged, enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the carrier network offload state for merged or unmerged networks for specified
+ * subscription.
+ * @param subscriptionId subscription ID see {@link SubscriptionInfo#getSubscriptionId()}
+ * @param merged True for carrier merged network, false otherwise.
+ * See {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)}
+ * @return True to indicate that carrier network offload is enabled, false otherwise.
+ * @see #setCarrierNetworkOffloadEnabled(int, boolean, boolean)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public boolean isCarrierNetworkOffloadEnabled(int subscriptionId, boolean merged) {
+ if (!SdkLevel.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mService.isCarrierNetworkOffloadEnabled(subscriptionId, merged);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index e606d533a532..b7450c538ff8 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -917,6 +917,9 @@ public final class WifiNetworkSuggestion implements Parcelable {
mPasspointConfiguration.setCarrierId(mCarrierId);
mPasspointConfiguration.setSubscriptionId(mSubscriptionId);
mPasspointConfiguration.setMeteredOverride(wifiConfiguration.meteredOverride);
+ mPasspointConfiguration.setOemPrivate(mIsNetworkOemPrivate);
+ mPasspointConfiguration.setOemPaid(mIsNetworkOemPaid);
+ mPasspointConfiguration.setCarrierMerged(mIsCarrierMerged);
wifiConfiguration.macRandomizationSetting = mIsEnhancedMacRandomizationEnabled
? WifiConfiguration.RANDOMIZATION_ENHANCED
: WifiConfiguration.RANDOMIZATION_PERSISTENT;
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index e19b095b27eb..540bf2a72b34 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -774,9 +774,13 @@ public class WifiAwareManager {
(byte[]) msg.obj);
break;
case CALLBACK_MATCH_EXPIRED:
+ if (!SdkLevel.isAtLeastS()) {
+ break;
+ }
mOriginalCallback
.onServiceLost(new PeerHandle(msg.arg1),
WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE);
+ break;
}
}
};
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 357c5bcfa265..006fbaa028bd 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -474,6 +474,27 @@ public final class PasspointConfiguration implements Parcelable {
*/
private boolean mIsEnhancedMacRandomizationEnabled = false;
+
+ /**
+ * Indicate whether the network is oem paid or not. Networks are considered oem paid
+ * if the corresponding connection is only available to system apps.
+ * @hide
+ */
+ private boolean mIsOemPaid;
+
+ /**
+ * Indicate whether the network is oem private or not. Networks are considered oem private
+ * if the corresponding connection is only available to system apps.
+ * @hide
+ */
+ private boolean mIsOemPrivate;
+
+ /**
+ * Indicate whether or not the network is a carrier merged network.
+ * @hide
+ */
+ private boolean mIsCarrierMerged;
+
/**
* Indicates if the end user has expressed an explicit opinion about the
* meteredness of this network, such as through the Settings app.
@@ -589,6 +610,54 @@ public final class PasspointConfiguration implements Parcelable {
}
/**
+ * Set whether the network is oem paid or not.
+ * @hide
+ */
+ public void setOemPaid(boolean isOemPaid) {
+ mIsOemPaid = isOemPaid;
+ }
+
+ /**
+ * Get whether the network is oem paid or not.
+ * @hide
+ */
+ public boolean isOemPaid() {
+ return mIsOemPaid;
+ }
+
+ /**
+ * Set whether the network is oem private or not.
+ * @hide
+ */
+ public void setOemPrivate(boolean isOemPrivate) {
+ mIsOemPrivate = isOemPrivate;
+ }
+
+ /**
+ * Get whether the network is oem private or not.
+ * @hide
+ */
+ public boolean isOemPrivate() {
+ return mIsOemPrivate;
+ }
+
+ /**
+ * Set whether the network is carrier merged or not.
+ * @hide
+ */
+ public void setCarrierMerged(boolean isCarrierMerged) {
+ mIsCarrierMerged = isCarrierMerged;
+ }
+
+ /**
+ * Get whether the network is carrier merged or not.
+ * @hide
+ */
+ public boolean isCarrierMerged() {
+ return mIsCarrierMerged;
+ }
+
+ /**
* Constructor for creating PasspointConfiguration with default values.
*/
public PasspointConfiguration() {}
@@ -635,6 +704,9 @@ public final class PasspointConfiguration implements Parcelable {
mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled;
mIsEnhancedMacRandomizationEnabled = source.mIsEnhancedMacRandomizationEnabled;
mMeteredOverride = source.mMeteredOverride;
+ mIsCarrierMerged = source.mIsCarrierMerged;
+ mIsOemPaid = source.mIsOemPaid;
+ mIsOemPrivate = source.mIsOemPrivate;
}
@Override
@@ -669,6 +741,9 @@ public final class PasspointConfiguration implements Parcelable {
dest.writeBoolean(mIsEnhancedMacRandomizationEnabled);
dest.writeInt(mMeteredOverride);
dest.writeInt(mSubscriptionId);
+ dest.writeBoolean(mIsCarrierMerged);
+ dest.writeBoolean(mIsOemPaid);
+ dest.writeBoolean(mIsOemPrivate);
}
@Override
@@ -700,6 +775,9 @@ public final class PasspointConfiguration implements Parcelable {
&& mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
&& mCarrierId == that.mCarrierId
&& mSubscriptionId == that.mSubscriptionId
+ && mIsOemPrivate == that.mIsOemPrivate
+ && mIsOemPaid == that.mIsOemPaid
+ && mIsCarrierMerged == that.mIsCarrierMerged
&& mIsAutojoinEnabled == that.mIsAutojoinEnabled
&& mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled
&& mIsEnhancedMacRandomizationEnabled == that.mIsEnhancedMacRandomizationEnabled
@@ -715,7 +793,8 @@ public final class PasspointConfiguration implements Parcelable {
mSubscriptionExpirationTimeMillis, mUsageLimitUsageTimePeriodInMinutes,
mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
mServiceFriendlyNames, mCarrierId, mIsAutojoinEnabled, mIsMacRandomizationEnabled,
- mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId);
+ mIsEnhancedMacRandomizationEnabled, mMeteredOverride, mSubscriptionId,
+ mIsCarrierMerged, mIsOemPaid, mIsOemPrivate);
}
@Override
@@ -774,6 +853,9 @@ public final class PasspointConfiguration implements Parcelable {
builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled);
builder.append("mIsEnhancedMacRandomizationEnabled:" + mIsEnhancedMacRandomizationEnabled);
builder.append("mMeteredOverride:" + mMeteredOverride);
+ builder.append("mIsCarrierMerged:" + mIsCarrierMerged);
+ builder.append("mIsOemPaid:" + mIsOemPaid);
+ builder.append("mIsOemPrivate" + mIsOemPrivate);
return builder.toString();
}
@@ -884,6 +966,10 @@ public final class PasspointConfiguration implements Parcelable {
config.mIsEnhancedMacRandomizationEnabled = in.readBoolean();
config.mMeteredOverride = in.readInt();
config.mSubscriptionId = in.readInt();
+ config.mIsCarrierMerged = in.readBoolean();
+ config.mIsOemPaid = in.readBoolean();
+ config.mIsOemPrivate = in.readBoolean();
+
return config;
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 39f6f57b05b3..52e91394d8bf 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -136,6 +136,7 @@ public class WifiManagerTest {
private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"};
private static final int TEST_AP_FREQUENCY = 2412;
private static final int TEST_AP_BANDWIDTH = SoftApInfo.CHANNEL_WIDTH_20MHZ;
+ private static final int TEST_SUB_ID = 3;
@Mock Context mContext;
@Mock android.net.wifi.IWifiManager mWifiService;
@@ -2583,10 +2584,29 @@ public class WifiManagerTest {
@Test
public void testGetNetworkSuggestionUserApprovalStatus() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastS());
+
when(mWifiService.getNetworkSuggestionUserApprovalStatus(TEST_PACKAGE_NAME))
.thenReturn(WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER);
assertEquals(WifiManager.STATUS_SUGGESTION_APPROVAL_APPROVED_BY_USER,
mWifiManager.getNetworkSuggestionUserApprovalStatus());
verify(mWifiService).getNetworkSuggestionUserApprovalStatus(TEST_PACKAGE_NAME);
}
+
+ @Test
+ public void testSetCarrierNetworkOffload() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastS());
+ mWifiManager.setCarrierNetworkOffloadEnabled(TEST_SUB_ID, true, false);
+ verify(mWifiService).setCarrierNetworkOffloadEnabled(TEST_SUB_ID,
+ true, false);
+ }
+
+ @Test
+ public void testGetCarrierNetworkOffload() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastS());
+ when(mWifiService.isCarrierNetworkOffloadEnabled(TEST_SUB_ID, false)).thenReturn(true);
+ assertTrue(mWifiManager.isCarrierNetworkOffloadEnabled(TEST_SUB_ID, false));
+ verify(mWifiService).isCarrierNetworkOffloadEnabled(TEST_SUB_ID, false);
+ }
+
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 643a78c38f91..5e829188f93f 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -809,7 +809,7 @@ public class WifiNetworkSuggestionTest {
/**
* Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
* when both {@link WifiNetworkSuggestion.Builder#setWpa3Passphrase(String)} and
- * {@link WifiNetworkSuggestion.Builderi
+ * {@link WifiNetworkSuggestion.Builder
* #setWpa3EnterpriseStandardModeConfig(WifiEnterpriseConfig)}
* are invoked.
*/
@@ -1310,6 +1310,7 @@ public class WifiNetworkSuggestionTest {
.build();
assertTrue(suggestion.isOemPaid());
assertFalse(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.getPasspointConfig().isOemPaid());
}
/**
@@ -1345,6 +1346,7 @@ public class WifiNetworkSuggestionTest {
.build();
assertTrue(suggestion.isOemPrivate());
assertFalse(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.getPasspointConfig().isOemPrivate());
}
/**
@@ -1439,6 +1441,25 @@ public class WifiNetworkSuggestionTest {
}
/**
+ * Validate {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} (boolean)} set the
+ * correct value to the passpoint network.
+ */
+ @Test
+ public void testSetCarrierMergedNetworkOnPasspointNetwork() {
+ assumeTrue(SdkLevel.isAtLeastS());
+
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration)
+ .setSubscriptionId(1)
+ .setCarrierMerged(true)
+ .setIsMetered(true)
+ .build();
+ assertTrue(suggestion.isCarrierMerged());
+ assertTrue(suggestion.getPasspointConfig().isCarrierMerged());
+ }
+
+ /**
* Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
* when set both {@link WifiNetworkSuggestion.Builder#setCarrierMerged(boolean)} (boolean)}
* to true on a network is not metered.