summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java13
-rw-r--r--cmds/statsd/Android.bp9
-rw-r--r--cmds/statsd/src/FieldValue.h15
-rw-r--r--cmds/statsd/src/HashableDimensionKey.cpp17
-rw-r--r--cmds/statsd/src/HashableDimensionKey.h12
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp3
-rw-r--r--cmds/statsd/src/logd/LogEvent.h17
-rw-r--r--cmds/statsd/src/main.cpp19
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp3
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp12
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h4
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h2
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp4
-rw-r--r--cmds/statsd/src/state/StateManager.cpp12
-rw-r--r--cmds/statsd/src/state/StateManager.h7
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp233
-rw-r--r--cmds/statsd/src/state/StateTracker.h41
-rw-r--r--cmds/statsd/src/stats_log_util.cpp13
-rw-r--r--cmds/statsd/src/stats_log_util.h7
-rw-r--r--cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp4
-rw-r--r--cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp10
-rw-r--r--cmds/statsd/tests/state/StateTracker_test.cpp28
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp34
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java38
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java3
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java17
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageRead.java8
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java28
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java6
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java12
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java3
-rw-r--r--core/java/android/hardware/camera2/legacy/LegacyResultMapper.java13
-rw-r--r--core/java/android/hardware/camera2/legacy/ParameterUtils.java101
-rw-r--r--core/java/android/view/NotificationHeaderView.java3
-rw-r--r--core/java/android/widget/Editor.java6
-rw-r--r--core/java/android/widget/TextView.java46
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java14
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java67
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java2
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java56
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java5
-rw-r--r--core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java11
-rw-r--r--core/java/com/android/internal/app/procstats/DumpUtils.java110
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessState.java136
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java67
-rw-r--r--core/java/com/android/internal/app/procstats/PssTable.java17
-rw-r--r--core/proto/android/service/procstats_enum.proto24
-rw-r--r--core/res/res/layout/notification_template_header.xml15
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml13
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/tests/coretests/src/android/widget/EditorCursorDragTest.java59
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java6
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java55
-rw-r--r--media/OWNERS4
-rw-r--r--media/jni/android_media_MediaCodec.cpp22
-rw-r--r--packages/SystemUI/res/drawable-nodpi/controls_btn_star.xml25
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star_filled.xml27
-rw-r--r--packages/SystemUI/res/drawable-nodpi/star_outline.xml27
-rw-r--r--packages/SystemUI/res/layout/controls_base_item.xml1
-rw-r--r--packages/SystemUI/res/layout/notification_conversation_info.xml2
-rw-r--r--packages/SystemUI/res/layout/notification_info.xml2
-rw-r--r--packages/SystemUI/res/layout/priority_onboarding_half_shell.xml194
-rw-r--r--packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl67
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml12
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java187
-rw-r--r--packages/SystemUI/src/com/android/systemui/Prefs.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java132
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java246
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java124
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt146
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java133
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java10
-rw-r--r--packages/Tethering/AndroidManifest.xml3
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04/strings.xml30
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004/config.xml20
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480/config.xml20
-rw-r--r--packages/Tethering/res/values/config.xml6
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java92
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java16
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java5
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java185
-rw-r--r--packages/Tethering/tests/unit/AndroidManifest.xml1
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt239
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java14
-rw-r--r--read-snapshot.txt0
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java36
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java19
-rw-r--r--services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java7
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java31
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java21
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java2
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java44
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java65
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java12
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java53
-rw-r--r--tests/UiBench/AndroidManifest.xml9
-rw-r--r--tests/UiBench/src/com/android/test/uibench/WindowInsetsControllerActivity.java53
-rw-r--r--tools/stats_log_api_gen/atoms_info_writer.cpp80
-rw-r--r--wifi/jarjar-rules.txt49
141 files changed, 2707 insertions, 1989 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f8b2f32e1a2f..ac58f3d6a94d 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1224,7 +1224,7 @@ public class DeviceIdleController extends SystemService
IDLE_FACTOR = mParser.getFloat(KEY_IDLE_FACTOR,
2f);
MIN_TIME_TO_ALARM = mParser.getDurationMillis(KEY_MIN_TIME_TO_ALARM,
- !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);
+ !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L);
MAX_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
KEY_MAX_TEMP_APP_WHITELIST_DURATION, 5 * 60 * 1000L);
MMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
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 07a99084e9d5..2aa2275cc67b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2182,17 +2182,18 @@ public class JobSchedulerService extends com.android.server.SystemService
}
final boolean jobExists = mJobs.containsJob(job);
-
final boolean userStarted = areUsersStartedLocked(job);
+ final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
- + " exists=" + jobExists + " userStarted=" + userStarted);
+ + " exists=" + jobExists + " userStarted=" + userStarted
+ + " backingUp=" + backingUp);
}
// These are also fairly cheap to check, though they typically will not
// be conditions we fail.
- if (!jobExists || !userStarted) {
+ if (!jobExists || !userStarted || backingUp) {
return false;
}
@@ -2265,15 +2266,17 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean jobExists = mJobs.containsJob(job);
final boolean userStarted = areUsersStartedLocked(job);
+ final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
if (DEBUG) {
Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
- + " exists=" + jobExists + " userStarted=" + userStarted);
+ + " exists=" + jobExists + " userStarted=" + userStarted
+ + " backingUp=" + backingUp);
}
// These are also fairly cheap to check, though they typically will not
// be conditions we fail.
- if (!jobExists || !userStarted) {
+ if (!jobExists || !userStarted || backingUp) {
return false;
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 6e8ceb7cb367..b4519b769b7c 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -291,7 +291,14 @@ cc_binary {
cc_test {
name: "statsd_test",
defaults: ["statsd_defaults"],
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
+
+ //TODO(b/153588990): Remove when the build system properly separates
+ //32bit and 64bit architectures.
+ multilib: {
+ lib32: { suffix: "32", },
+ lib64: { suffix: "64", },
+ },
cflags: [
"-Wall",
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 92e09ea0f8f9..e251399776fb 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -181,6 +181,7 @@ public:
return false;
}
+
bool matches(const Matcher& that) const;
};
@@ -360,7 +361,9 @@ struct Value {
class Annotations {
public:
- Annotations() {}
+ 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.
@@ -379,7 +382,9 @@ public:
inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
- inline void setResetState(int resetState) { mResetState = resetState; }
+ inline void setResetState(int32_t resetState) {
+ mResetState = resetState;
+ }
// Default value = false
inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
@@ -395,7 +400,9 @@ public:
// If a reset state is not sent in the StatsEvent, returns -1. Note that a
// reset satate is only sent if and only if a reset should be triggered.
- inline int getResetState() const { return mResetState; }
+ inline int32_t getResetState() const {
+ return mResetState;
+ }
private:
inline void setBitmaskAtPos(int pos, bool value) {
@@ -411,7 +418,7 @@ private:
// there are only 4 booleans, just one byte is required.
uint8_t mBooleanBitmask = 0;
- int mResetState = -1;
+ int32_t mResetState = -1;
};
/**
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 29249f4a6c55..eba66e0cb7b0 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -180,6 +180,23 @@ bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>
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) {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 33a502497746..bd011005a301 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -154,6 +154,18 @@ bool filterValues(const std::vector<Matcher>& matcherFields, const std::vector<F
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
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 8b6a86464155..61cd01728ab1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -293,7 +293,8 @@ void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
}
const bool exclusiveState = readNextValue<uint8_t>();
- mValues[mValues.size() - 1].mAnnotations.setExclusiveState(exclusiveState);
+ mExclusiveStateFieldIndex = mValues.size() - 1;
+ mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
}
void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 4eeb7d64a463..41fdcc2cbe7a 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -145,7 +145,7 @@ public:
}
// Default value = false
- inline bool shouldTruncateTimestamp() {
+ inline bool shouldTruncateTimestamp() const {
return mTruncateTimestamp;
}
@@ -170,6 +170,20 @@ public:
return mAttributionChainIndex;
}
+ // 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 mExclusiveStateFieldIndex;
+ }
+
inline LogEvent makeCopy() {
return LogEvent(*this);
}
@@ -297,6 +311,7 @@ private:
bool mTruncateTimestamp = false;
int mUidFieldIndex = -1;
int mAttributionChainIndex = -1;
+ int mExclusiveStateFieldIndex = -1;
};
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index d79b6a21c19f..e3945334aeca 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -38,20 +38,27 @@ using std::make_shared;
shared_ptr<StatsService> gStatsService = nullptr;
-void sigHandler(int sig) {
- if (gStatsService != nullptr) {
- gStatsService->Terminate();
+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 (gStatsService != nullptr) gStatsService->Terminate();
ALOGW("statsd terminated on receiving signal %d.", sig);
exit(1);
}
-void registerSigHandler()
+void registerSignalHandlers()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
- sa.sa_handler = sigHandler;
+ sa.sa_handler = signalHandler;
+ sigaction(SIGPIPE, &sa, nullptr);
sigaction(SIGHUP, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGQUIT, &sa, nullptr);
@@ -79,7 +86,7 @@ int main(int /*argc*/, char** /*argv*/) {
return -1;
}
- registerSigHandler();
+ registerSignalHandlers();
gStatsService->sayHiToStatsCompanion();
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 6833f8dd0114..d68f64ae40b0 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -151,8 +151,7 @@ void EventMetricProducer::onMatchedLogEventInternalLocked(
uint64_t wrapperToken =
mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- const int64_t elapsedTimeNs = truncateTimestampIfNecessary(
- event.GetTagId(), event.GetElapsedTimestampNs());
+ 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);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 42bbd8eb4d35..c4bd0549465a 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -270,11 +270,9 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->end(atomsToken);
}
for (const auto& atom : bucket.mGaugeAtoms) {
- const int64_t elapsedTimestampNs =
- truncateTimestampIfNecessary(mAtomId, atom.mElapsedTimestamps);
- protoOutput->write(
- FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
- (long long)elapsedTimestampNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
+ FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
+ (long long)atom.mElapsedTimestampNs);
}
}
protoOutput->end(bucketInfoToken);
@@ -477,7 +475,9 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked(
if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
return;
}
- GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
+
+ 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.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 79ec71120f18..aa0cae26080d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -35,10 +35,10 @@ namespace statsd {
struct GaugeAtom {
GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
- : mFields(fields), mElapsedTimestamps(elapsedTimeNs) {
+ : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
- int64_t mElapsedTimestamps;
+ int64_t mElapsedTimestampNs;
};
struct GaugeBucket {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 4550e65b6438..6aba13ca7859 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -451,7 +451,7 @@ protected:
std::vector<int32_t> mSlicedStateAtoms;
// Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
- std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
+ 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.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 2fcb13b709f9..88616dde61b7 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -795,9 +795,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
for (const auto& it : allMetricProducers) {
// Register metrics to StateTrackers
for (int atomId : it->getSlicedStateAtoms()) {
- if (!StateManager::getInstance().registerListener(atomId, it)) {
- return false;
- }
+ StateManager::getInstance().registerListener(atomId, it);
}
}
return true;
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index ea776fae0583..5514b446b306 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -38,20 +38,12 @@ void StateManager::onLogEvent(const LogEvent& event) {
}
}
-bool StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
+void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
// Check if state tracker already exists.
if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
- // Create a new state tracker iff atom is a state atom.
- auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
- if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
- mStateTrackers[atomId] = new StateTracker(atomId, it->second);
- } else {
- ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId);
- return false;
- }
+ mStateTrackers[atomId] = new StateTracker(atomId);
}
mStateTrackers[atomId]->registerListener(listener);
- return true;
}
void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index 8b3a4218238c..577a0f51e38b 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -45,10 +45,11 @@ public:
// Notifies the correct StateTracker of an event.
void onLogEvent(const LogEvent& event);
- // Returns true if atomId is being tracked and is associated with a state
- // atom. StateManager notifies the correct StateTracker to register listener.
+ // Notifies the StateTracker for the given atomId to register listener.
// If the correct StateTracker does not exist, a new StateTracker is created.
- bool registerListener(const int32_t atomId, wp<StateListener> listener);
+ // 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.
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index ab861275073c..b7f314a819df 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -25,81 +25,43 @@ namespace android {
namespace os {
namespace statsd {
-StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo)
- : mAtomId(atomId),
- mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)),
- mNested(stateAtomInfo.nested) {
- // create matcher for each primary field
- for (const auto& primaryField : stateAtomInfo.primaryFields) {
- if (primaryField == util::FIRST_UID_IN_CHAIN) {
- Matcher matcher = getFirstUidMatcher(atomId);
- mPrimaryFields.push_back(matcher);
- } else {
- Matcher matcher = getSimpleMatcher(atomId, primaryField);
- mPrimaryFields.push_back(matcher);
- }
- }
-
- if (stateAtomInfo.defaultState != util::UNSET_VALUE) {
- mDefaultState = stateAtomInfo.defaultState;
- }
-
- if (stateAtomInfo.resetState != util::UNSET_VALUE) {
- mResetState = stateAtomInfo.resetState;
- }
+StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
}
void StateTracker::onLogEvent(const LogEvent& event) {
- int64_t eventTimeNs = event.GetElapsedTimestampNs();
+ const int64_t eventTimeNs = event.GetElapsedTimestampNs();
// Parse event for primary field values i.e. primary key.
HashableDimensionKey primaryKey;
- if (mPrimaryFields.size() > 0) {
- if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) ||
- primaryKey.getValues().size() != mPrimaryFields.size()) {
- ALOGE("StateTracker error extracting primary key from log event.");
- handleReset(eventTimeNs);
- return;
- }
- } else {
- // Use an empty HashableDimensionKey if atom has no primary fields.
- primaryKey = DEFAULT_DIMENSION_KEY;
- }
+ filterPrimaryKey(event.getValues(), &primaryKey);
- // Parse event for state value.
FieldValue stateValue;
- if (!filterValues(mStateField, event.getValues(), &stateValue) ||
- stateValue.mValue.getType() != INT) {
+ if (!getStateFieldValueFromLogEvent(event, &stateValue)) {
+ ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
+ return;
+ }
+
+ mField.setField(stateValue.mField.getField());
+
+ if (stateValue.mValue.getType() != INT) {
ALOGE("StateTracker error extracting state from log event. Type: %d",
stateValue.mValue.getType());
- handlePartialReset(eventTimeNs, primaryKey);
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
return;
}
- int32_t state = stateValue.mValue.int_value;
- if (state == mResetState) {
- VLOG("StateTracker Reset state: %s", stateValue.mValue.toString().c_str());
- handleReset(eventTimeNs);
+ const int32_t resetState = stateValue.mAnnotations.getResetState();
+ if (resetState != -1) {
+ VLOG("StateTracker new reset state: %d", resetState);
+ handleReset(eventTimeNs, resetState);
return;
}
- // Track and update state.
- int32_t oldState = 0;
- int32_t newState = 0;
- updateState(primaryKey, state, &oldState, &newState);
-
- // Notify all listeners if state has changed.
- if (oldState != newState) {
- VLOG("StateTracker updated state");
- for (auto listener : mListeners) {
- auto sListener = listener.promote(); // safe access to wp<>
- if (sListener != nullptr) {
- sListener->onStateChanged(eventTimeNs, mAtomId, primaryKey, oldState, newState);
- }
- }
- } else {
- VLOG("StateTracker NO updated state");
- }
+ const int32_t newState = stateValue.mValue.int_value;
+ const bool nested = stateValue.mAnnotations.isNested();
+ StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
}
void StateTracker::registerListener(wp<StateListener> listener) {
@@ -111,81 +73,62 @@ void StateTracker::unregisterListener(wp<StateListener> listener) {
}
bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
- output->mField = mStateField.mMatcher;
-
- // Check that the query key has the correct number of primary fields.
- if (queryKey.getValues().size() == mPrimaryFields.size()) {
- auto it = mStateMap.find(queryKey);
- if (it != mStateMap.end()) {
- output->mValue = it->second.state;
- return true;
- }
- } else if (queryKey.getValues().size() > mPrimaryFields.size()) {
- ALOGE("StateTracker query key size %zu > primary key size %zu is illegal",
- queryKey.getValues().size(), mPrimaryFields.size());
- } else {
- ALOGE("StateTracker query key size %zu < primary key size %zu is not supported",
- queryKey.getValues().size(), mPrimaryFields.size());
+ 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 default state if:
- // - query key size is incorrect
- // - query key is not found in state map
- output->mValue = mDefaultState;
+ // 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) {
+void StateTracker::handleReset(const int64_t eventTimeNs, const int32_t newState) {
VLOG("StateTracker handle reset");
- for (const auto pair : mStateMap) {
- for (auto l : mListeners) {
- auto sl = l.promote();
- if (sl != nullptr) {
- sl->onStateChanged(eventTimeNs, mAtomId, pair.first, pair.second.state,
- mDefaultState);
- }
- }
+ for (auto& [primaryKey, stateValueInfo] : mStateMap) {
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
+ false /* nested; treat this state change as not nested */,
+ &stateValueInfo);
}
- mStateMap.clear();
}
-void StateTracker::handlePartialReset(const int64_t eventTimeNs,
- const HashableDimensionKey& primaryKey) {
- VLOG("StateTracker handle partial reset");
- if (mStateMap.find(primaryKey) != mStateMap.end()) {
- for (auto l : mListeners) {
- auto sl = l.promote();
- if (sl != nullptr) {
- sl->onStateChanged(eventTimeNs, mAtomId, primaryKey,
- mStateMap.find(primaryKey)->second.state, mDefaultState);
- }
- }
- mStateMap.erase(primaryKey);
+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.
+ if (it != mStateMap.end()) {
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, kStateUnknown,
+ false /* nested; treat this state change as not nested */,
+ &it->second);
}
}
-void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
- int32_t* oldState, int32_t* newState) {
- // get old state (either current state in map or default state)
- auto it = mStateMap.find(primaryKey);
- if (it != mStateMap.end()) {
- *oldState = it->second.state;
- } else {
- *oldState = mDefaultState;
+void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey,
+ const int32_t newState, const bool nested,
+ StateValueInfo* stateValueInfo) {
+ const int32_t oldState = stateValueInfo->state;
+
+ if (kStateUnknown == newState) {
+ mStateMap.erase(primaryKey);
}
// Update state map for non-nested counting case.
// Every state event triggers a state overwrite.
- if (!mNested) {
- if (eventState == mDefaultState) {
- // remove (key, state) pair if state returns to default state
- VLOG("\t StateTracker changed to default state")
- mStateMap.erase(primaryKey);
- } else {
- mStateMap[primaryKey].state = eventState;
- mStateMap[primaryKey].count = 1;
+ if (!nested) {
+ stateValueInfo->state = newState;
+ stateValueInfo->count = 1;
+
+ // Notify listeners if state has changed.
+ if (oldState != newState) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
- *newState = eventState;
return;
}
@@ -197,31 +140,47 @@ void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int
// number of OFF events as ON events.
//
// In atoms.proto, a state atom with nested counting enabled
- // must only have 2 states and one of the states must be the default state.
- it = mStateMap.find(primaryKey);
- if (it != mStateMap.end()) {
- *newState = it->second.state;
- if (eventState == it->second.state) {
- it->second.count++;
- } else if (eventState == mDefaultState) {
- if ((--it->second.count) == 0) {
- mStateMap.erase(primaryKey);
- *newState = mDefaultState;
- }
- } else {
- ALOGE("StateTracker Nest counting state has a third state instead of the binary state "
- "limit.");
- return;
+ // must only have 2 states. There is no enforcemnt here of this requirement.
+ // The atom must be logged correctly.
+ if (kStateUnknown == newState) {
+ if (kStateUnknown != oldState) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
}
- } else {
- if (eventState != mDefaultState) {
- mStateMap[primaryKey].state = eventState;
- mStateMap[primaryKey].count = 1;
+ } else if (oldState == kStateUnknown) {
+ stateValueInfo->state = newState;
+ stateValueInfo->count = 1;
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ } else if (oldState == newState) {
+ stateValueInfo->count++;
+ } else if (--stateValueInfo->count == 0) {
+ stateValueInfo->state = newState;
+ stateValueInfo->count = 1;
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ }
+}
+
+void StateTracker::notifyListeners(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey, const int32_t oldState,
+ const int32_t newState) {
+ for (auto l : mListeners) {
+ auto sl = l.promote();
+ if (sl != nullptr) {
+ sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
}
- *newState = eventState;
}
}
+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
index 154750e6da3d..c5f6315fc992 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -15,7 +15,6 @@
*/
#pragma once
-#include <atoms_info.h>
#include <utils/RefBase.h>
#include "HashableDimensionKey.h"
#include "logd/LogEvent.h"
@@ -30,7 +29,7 @@ namespace statsd {
class StateTracker : public virtual RefBase {
public:
- StateTracker(const int32_t atomId, const android::util::StateAtomFieldOptions& stateAtomInfo);
+ StateTracker(const int32_t atomId);
virtual ~StateTracker(){};
@@ -60,21 +59,11 @@ public:
private:
struct StateValueInfo {
- int32_t state; // state value
- int count; // nested count (only used for binary states)
+ int32_t state = kStateUnknown; // state value
+ int count = 0; // nested count (only used for binary states)
};
- const int32_t mAtomId; // id of the state atom being tracked
-
- Matcher mStateField; // matches the atom's exclusive state field
-
- std::vector<Matcher> mPrimaryFields; // matches the atom's primary fields
-
- int32_t mDefaultState = kStateUnknown;
-
- int32_t mResetState = kStateUnknown;
-
- const bool mNested;
+ Field mField;
// Maps primary key to state value info
std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
@@ -82,20 +71,24 @@ private:
// Set of all StateListeners (objects listening for state changes)
std::set<wp<StateListener>> mListeners;
- // Reset all state values in map to default state.
- void handleReset(const int64_t eventTimeNs);
+ // Reset all state values in map to the given state.
+ void handleReset(const int64_t eventTimeNs, const int32_t newState);
- // Reset only the state value mapped to the given primary key to default state.
- // Partial resets are used when we only need to update the state of one primary
- // key instead of clearing/reseting every key in the map.
- void handlePartialReset(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
+ // 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.
- // Store the old and new states.
- void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
- int32_t* oldState, int32_t* newState);
+ void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+ const int32_t newState, const bool nested,
+ StateValueInfo* stateValueInfo);
+
+ // Notify registered state listeners of state change.
+ void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+ const int32_t oldState, const int32_t newState);
};
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 77a3eb31fdd4..563531351fe0 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -549,14 +549,13 @@ int64_t getWallClockMillis() {
return time(nullptr) * MS_PER_SEC;
}
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs) {
- if (AtomsInfo::kTruncatingTimestampAtomBlackList.find(atomId) !=
- AtomsInfo::kTruncatingTimestampAtomBlackList.end() ||
- (atomId >= StatsdStats::kTimestampTruncationStartTag &&
- atomId <= StatsdStats::kTimestampTruncationEndTag)) {
- return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
+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 timestampNs;
+ return event.GetElapsedTimestampNs();
}
}
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index ade25d69a3a4..20d93b5a5365 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -17,11 +17,12 @@
#pragma once
#include <android/util/ProtoOutputStream.h>
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
-#include "atoms_info.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
using android::util::ProtoOutputStream;
@@ -93,9 +94,9 @@ bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) {
return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
}
-// Checks the blacklist of atoms as well as the blacklisted range of 300,000 - 304,999.
+// Checks the truncate timestamp annotation as well as the blacklisted range of 300,000 - 304,999.
// Returns the truncated timestamp to the nearest 5 minutes if needed.
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs);
+int64_t truncateTimestampIfNecessary(const LogEvent& event);
// Checks permission for given pid and uid.
bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid);
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
index a5da9c8a6f56..b1461a1535ba 100644
--- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -232,7 +232,7 @@ TEST(CountMetricE2eTest, TestSlicedStateWithMap) {
StateMap map = state.map();
for (auto group : map.group()) {
for (auto value : group.value()) {
- EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+ EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
group.group_id());
}
}
@@ -614,7 +614,7 @@ TEST(CountMetricE2eTest, TestMultipleSlicedStates) {
StateMap map = state1.map();
for (auto group : map.group()) {
for (auto value : group.value()) {
- EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][value],
+ EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
group.group_id());
}
}
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index ba09a353e8b6..d59ec3ef4a29 100644
--- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -921,18 +921,18 @@ TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) {
bucket #1 bucket #2
| 1 2 3 4 5 6 7 8 (minutes)
|---------------------------------------|------------------
- ON OFF ON (BatterySaverMode)
+ ON OFF ON (BatterySaverMode)
T F T (DeviceUnpluggedPredicate)
- | | | (ScreenIsOnEvent)
+ | | | (ScreenIsOnEvent)
| | | (ScreenIsOffEvent)
| (ScreenDozeEvent)
*/
// Initialize log events.
std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
events.push_back(CreateScreenStateChangedEvent(
- bucketStartTimeNs + 60 * NS_PER_SEC,
- android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:10
+ 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
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index 78c80bc8307c..ba2a4cfbe8fd 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -19,6 +19,7 @@
#include "state/StateListener.h"
#include "state/StateManager.h"
+#include "state/StateTracker.h"
#include "stats_event.h"
#include "tests/statsd_test_util.h"
@@ -127,23 +128,23 @@ TEST(StateTrackerTest, TestRegisterListener) {
// Register listener to non-existing StateTracker
EXPECT_EQ(0, mgr.getStateTrackersCount());
- EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1));
+ 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
- EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2));
+ 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
- EXPECT_TRUE(mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2));
+ 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
- EXPECT_FALSE(mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2));
- EXPECT_EQ(1, mgr.getStateTrackersCount());
+ mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2);
+ EXPECT_EQ(2, mgr.getStateTrackersCount());
}
/**
@@ -249,6 +250,9 @@ TEST(StateTrackerTest, TestStateChangeReset) {
EXPECT_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 =
@@ -258,6 +262,8 @@ TEST(StateTrackerTest, TestStateChangeReset) {
EXPECT_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 =
@@ -265,8 +271,12 @@ TEST(StateTrackerTest, TestStateChangeReset) {
BleScanStateChanged::RESET, false, false, false);
mgr.onLogEvent(*event3);
EXPECT_EQ(2, listener->updates.size());
- EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
- EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
+ 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);
+ }
}
/**
@@ -352,13 +362,13 @@ TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
// No state stored for this query key.
HashableDimensionKey queryKey2;
getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
- EXPECT_EQ(WakelockStateChanged::RELEASE,
+ EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
// Partial query fails.
HashableDimensionKey queryKey3;
getPartialWakelockKey(1001 /* uid */, &queryKey3);
- EXPECT_EQ(WakelockStateChanged::RELEASE,
+ EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
}
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 687014f6a298..7216e1d8cc8e 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -569,6 +569,8 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
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>(/*uid=*/0, /*pid=*/0);
parseStatsEventToLogEvent(statsEvent, logEvent.get());
@@ -662,9 +664,14 @@ std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs,
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());
@@ -803,7 +810,11 @@ std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
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());
@@ -821,10 +832,20 @@ std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_writeInt32(statsEvent, filtered); // filtered
- AStatsEvent_writeInt32(statsEvent, firstMatch); // first match
- AStatsEvent_writeInt32(statsEvent, opportunistic); // opportunistic
+ 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());
@@ -840,9 +861,14 @@ std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, co
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_writeInt32(statsEvent, usingAlertWindow);
+ 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());
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index f216db6fc717..29a98faf5cd1 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -980,16 +980,10 @@ public final class BluetoothAdapter {
@Override
protected Integer recompute(Void query) {
try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- return mService.getState();
- }
+ return mService.getState();
} catch (RemoteException e) {
- Log.e(TAG, "", e);
- } finally {
- mServiceLock.readLock().unlock();
+ throw e.rethrowFromSystemServer();
}
- return BluetoothAdapter.STATE_OFF;
}
};
@@ -1004,6 +998,30 @@ public final class BluetoothAdapter {
}
/**
+ * Fetch the current bluetooth state. If the service is down, return
+ * OFF.
+ */
+ @AdapterState
+ private int getStateInternal() {
+ int state = BluetoothAdapter.STATE_OFF;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ state = mBluetoothGetStateCache.query(null);
+ }
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof RemoteException) {
+ Log.e(TAG, "", e.getCause());
+ } else {
+ throw e;
+ }
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return state;
+ }
+
+ /**
* Get the current state of the local Bluetooth adapter.
* <p>Possible return values are
* {@link #STATE_OFF},
@@ -1016,7 +1034,7 @@ public final class BluetoothAdapter {
@RequiresPermission(Manifest.permission.BLUETOOTH)
@AdapterState
public int getState() {
- int state = mBluetoothGetStateCache.query(null);
+ int state = getStateInternal();
// Consider all internal states as OFF
if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
@@ -1054,7 +1072,7 @@ public final class BluetoothAdapter {
@UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
+ "whether you can use BLE & BT classic.")
public int getLeState() {
- int state = mBluetoothGetStateCache.query(null);
+ int state = getStateInternal();
if (VDBG) {
Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 4c9553249a0c..2ee0ad67b108 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -34,6 +34,7 @@ import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
import android.os.Bundle;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import java.security.PublicKey;
import java.util.Map;
@@ -258,6 +259,8 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName);
+ ParsingPackage setMinExtensionVersions(@Nullable SparseIntArray minExtensionVersions);
+
ParsingPackage setMinSdkVersion(int minSdkVersion);
ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index be1817d09155..1a1395ca7e9e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -51,6 +51,7 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -340,6 +341,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
private String manageSpaceActivityName;
private float maxAspectRatio;
private float minAspectRatio;
+ @Nullable
+ private SparseIntArray minExtensionVersions;
private int minSdkVersion;
private int networkSecurityConfigRes;
@Nullable
@@ -1100,6 +1103,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
dest.writeBoolean(this.preserveLegacyExternalStorage);
dest.writeArraySet(this.mimeGroups);
dest.writeInt(this.gwpAsanMode);
+ dest.writeSparseIntArray(this.minExtensionVersions);
}
public ParsingPackageImpl(Parcel in) {
@@ -1259,6 +1263,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
this.preserveLegacyExternalStorage = in.readBoolean();
this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
this.gwpAsanMode = in.readInt();
+ this.minExtensionVersions = in.readSparseIntArray();
}
public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
@@ -1767,6 +1772,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
return minAspectRatio;
}
+ @Nullable
+ @Override
+ public SparseIntArray getMinExtensionVersions() {
+ return minExtensionVersions;
+ }
+
@Override
public int getMinSdkVersion() {
return minSdkVersion;
@@ -2215,6 +2226,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
}
@Override
+ public ParsingPackageImpl setMinExtensionVersions(@Nullable SparseIntArray value) {
+ minExtensionVersions = value;
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl setMinSdkVersion(int value) {
minSdkVersion = value;
return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 687bc235cfa7..1ded8d40c727 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -41,6 +41,7 @@ import android.os.Parcelable;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.R;
@@ -609,6 +610,13 @@ public interface ParsingPackageRead extends Parcelable {
String getManageSpaceActivityName();
/**
+ * @see ApplicationInfo#minExtensionVersions
+ * @see R.styleable#AndroidManifestExtensionSdk
+ */
+ @Nullable
+ SparseIntArray getMinExtensionVersions();
+
+ /**
* @see ApplicationInfo#minSdkVersion
* @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
*/
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index c61362feaeae..29ece4924e5c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -95,6 +95,7 @@ import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.apk.ApkSignatureVerifier;
@@ -1255,6 +1256,7 @@ public class ParsingPackageUtils {
int type;
final int innerDepth = parser.getDepth();
+ SparseIntArray minExtensionVersions = null;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1263,7 +1265,10 @@ public class ParsingPackageUtils {
final ParseResult result;
if (parser.getName().equals("extension-sdk")) {
- result = parseExtensionSdk(input, pkg, res, parser);
+ if (minExtensionVersions == null) {
+ minExtensionVersions = new SparseIntArray();
+ }
+ result = parseExtensionSdk(input, res, parser, minExtensionVersions);
XmlUtils.skipCurrentTag(parser);
} else {
result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
@@ -1273,6 +1278,7 @@ public class ParsingPackageUtils {
return input.error(result);
}
}
+ pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
} finally {
sa.recycle();
}
@@ -1280,8 +1286,21 @@ public class ParsingPackageUtils {
return input.success(pkg);
}
- private static ParseResult parseExtensionSdk(ParseInput input, ParsingPackage pkg,
- Resources res, XmlResourceParser parser) {
+ @Nullable
+ private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
+ if (input == null) {
+ return null;
+ }
+ SparseIntArray output = new SparseIntArray(input.size());
+ for (int i = 0; i < input.size(); i++) {
+ output.put(input.keyAt(i), input.valueAt(i));
+ }
+ return output;
+ }
+
+ private static ParseResult<SparseIntArray> parseExtensionSdk(
+ ParseInput input, Resources res, XmlResourceParser parser,
+ SparseIntArray minExtensionVersions) {
int sdkVersion;
int minVersion;
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
@@ -1316,7 +1335,8 @@ public class ParsingPackageUtils {
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Specified sdkVersion " + sdkVersion + " is not valid");
}
- return input.success(pkg);
+ minExtensionVersions.put(sdkVersion, minVersion);
+ return input.success(minExtensionVersions);
}
/**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
index 882a7f4ab37d..b3b4549426f0 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java
@@ -233,8 +233,10 @@ public class LegacyFaceDetectMapper {
Camera.Parameters params = legacyRequest.parameters;
Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
- request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+ ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
+ request.get(CaptureRequest.SCALER_CROP_REGION),
+ request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+ previewSize, params);
List<Face> convertedFaces = new ArrayList<>();
if (faces != null) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 6953a5b793c3..362ddfae67bf 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -771,6 +771,7 @@ public class LegacyMetadataMapper {
CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES ,
CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE ,
CameraCharacteristics.CONTROL_MAX_REGIONS ,
+ CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE ,
CameraCharacteristics.FLASH_INFO_AVAILABLE ,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL ,
CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES ,
@@ -828,6 +829,7 @@ public class LegacyMetadataMapper {
CaptureRequest.CONTROL_MODE,
CaptureRequest.CONTROL_SCENE_MODE,
CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
+ CaptureRequest.CONTROL_ZOOM_RATIO,
CaptureRequest.FLASH_MODE,
CaptureRequest.JPEG_GPS_COORDINATES,
CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
@@ -872,6 +874,7 @@ public class LegacyMetadataMapper {
CaptureResult.CONTROL_AWB_MODE ,
CaptureResult.CONTROL_AWB_LOCK ,
CaptureResult.CONTROL_MODE ,
+ CaptureResult.CONTROL_ZOOM_RATIO ,
CaptureResult.FLASH_MODE ,
CaptureResult.JPEG_GPS_COORDINATES ,
CaptureResult.JPEG_GPS_PROCESSING_METHOD ,
@@ -935,6 +938,12 @@ public class LegacyMetadataMapper {
private static void mapScaler(CameraMetadataNative m, Parameters p) {
/*
+ * control.zoomRatioRange
+ */
+ Range<Float> zoomRatioRange = new Range<Float>(1.0f, ParameterUtils.getMaxZoomRatio(p));
+ m.set(CONTROL_ZOOM_RATIO_RANGE, zoomRatioRange);
+
+ /*
* scaler.availableMaxDigitalZoom
*/
m.set(SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, ParameterUtils.getMaxZoomRatio(p));
@@ -1383,6 +1392,9 @@ public class LegacyMetadataMapper {
// control.sceneMode -- DISABLED is always available
m.set(CaptureRequest.CONTROL_SCENE_MODE, CONTROL_SCENE_MODE_DISABLED);
+ // control.zoomRatio -- 1.0
+ m.set(CaptureRequest.CONTROL_ZOOM_RATIO, 1.0f);
+
/*
* statistics.*
*/
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 2e06d5fb3b6f..3a46379477e9 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -68,8 +68,9 @@ public class LegacyRequestMapper {
*/
ParameterUtils.ZoomData zoomData;
{
- zoomData = ParameterUtils.convertScalerCropRegion(activeArray,
+ zoomData = ParameterUtils.convertToLegacyZoom(activeArray,
request.get(SCALER_CROP_REGION),
+ request.get(CONTROL_ZOOM_RATIO),
previewSize,
params);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index dc5823d80f21..09edf74f0d4c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -118,8 +118,10 @@ public class LegacyResultMapper {
Rect activeArraySize = characteristics.get(
CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArraySize,
- request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params);
+ ZoomData zoomData = ParameterUtils.convertToLegacyZoom(activeArraySize,
+ request.get(CaptureRequest.SCALER_CROP_REGION),
+ request.get(CaptureRequest.CONTROL_ZOOM_RATIO),
+ previewSize, params);
/*
* colorCorrection
@@ -516,5 +518,12 @@ public class LegacyResultMapper {
{
m.set(SCALER_CROP_REGION, zoomData.reportedCrop);
}
+
+ /*
+ * control.zoomRatio
+ */
+ {
+ m.set(CONTROL_ZOOM_RATIO, zoomData.reportedZoomRatio);
+ }
}
}
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 3cfd020aeee3..eb435989e9a0 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -73,11 +73,15 @@ public class ParameterUtils {
public final Rect previewCrop;
/** Reported crop-region given the zoom index, coordinates relative to active-array */
public final Rect reportedCrop;
+ /** Reported zoom ratio given the zoom index */
+ public final float reportedZoomRatio;
- public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop) {
+ public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop,
+ float reportedZoomRatio) {
this.zoomIndex = zoomIndex;
this.previewCrop = previewCrop;
this.reportedCrop = reportedCrop;
+ this.reportedZoomRatio = reportedZoomRatio;
}
}
@@ -371,7 +375,8 @@ public class ParameterUtils {
* @throws NullPointerException if any of the args were {@code null}
*/
public static int getClosestAvailableZoomCrop(
- Camera.Parameters params, Rect activeArray, Size streamSize, Rect cropRegion,
+ Camera.Parameters params, Rect activeArray,
+ Size streamSize, Rect cropRegion,
/*out*/
Rect reportedCropRegion,
Rect previewCropRegion) {
@@ -733,6 +738,92 @@ public class ParameterUtils {
}
/**
+ * Convert the user-specified crop region/zoom into zoom data; which can be used
+ * to set the parameters to a specific zoom index, or to report back to the user what
+ * the actual zoom was, or for other calculations requiring the current preview crop region.
+ *
+ * <p>None of the parameters are mutated.<p>
+ *
+ * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+ * @param cropRegion the user-specified crop region
+ * @param zoomRatio the user-specified zoom ratio
+ * @param previewSize the current preview size (in pixels)
+ * @param params the current camera parameters (not mutated)
+ *
+ * @return the zoom index, and the effective/reported crop regions (relative to active array)
+ */
+ public static ZoomData convertToLegacyZoom(Rect activeArraySize, Rect
+ cropRegion, Float zoomRatio, Size previewSize, Camera.Parameters params) {
+ final float FLOAT_EQUAL_THRESHOLD = 0.0001f;
+ if (zoomRatio != null &&
+ Math.abs(1.0f - zoomRatio) > FLOAT_EQUAL_THRESHOLD) {
+ // User uses CONTROL_ZOOM_RATIO to control zoom
+ return convertZoomRatio(activeArraySize, zoomRatio, previewSize, params);
+ }
+
+ return convertScalerCropRegion(activeArraySize, cropRegion, previewSize, params);
+ }
+
+ /**
+ * Convert the user-specified zoom ratio into zoom data; which can be used
+ * to set the parameters to a specific zoom index, or to report back to the user what the
+ * actual zoom was, or for other calculations requiring the current preview crop region.
+ *
+ * <p>None of the parameters are mutated.</p>
+ *
+ * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
+ * @param zoomRatio the current zoom ratio
+ * @param previewSize the current preview size (in pixels)
+ * @param params the current camera parameters (not mutated)
+ *
+ * @return the zoom index, and the effective/reported crop regions (relative to active array)
+ */
+ public static ZoomData convertZoomRatio(Rect activeArraySize, float zoomRatio,
+ Size previewSize, Camera.Parameters params) {
+ if (DEBUG) {
+ Log.v(TAG, "convertZoomRatio - user zoom ratio was " + zoomRatio);
+ }
+
+ List<Rect> availableReportedCropRegions =
+ getAvailableZoomCropRectangles(params, activeArraySize);
+ List<Rect> availablePreviewCropRegions =
+ getAvailablePreviewZoomCropRectangles(params, activeArraySize, previewSize);
+ if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
+ throw new AssertionError("available reported/preview crop region size mismatch");
+ }
+
+ // Find the best matched legacy zoom ratio for the requested camera2 zoom ratio.
+ int bestZoomIndex = 0;
+ Rect reportedCropRegion = new Rect(availableReportedCropRegions.get(0));
+ Rect previewCropRegion = new Rect(availablePreviewCropRegions.get(0));
+ float reportedZoomRatio = 1.0f;
+ if (params.isZoomSupported()) {
+ List<Integer> zoomRatios = params.getZoomRatios();
+ for (int i = 1; i < zoomRatios.size(); i++) {
+ if (zoomRatio * ZOOM_RATIO_MULTIPLIER >= zoomRatios.get(i)) {
+ bestZoomIndex = i;
+ reportedCropRegion = availableReportedCropRegions.get(i);
+ previewCropRegion = availablePreviewCropRegions.get(i);
+ reportedZoomRatio = zoomRatios.get(i);
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, "convertZoomRatio - zoom calculated to: " +
+ "zoomIndex = " + bestZoomIndex +
+ ", reported crop region = " + reportedCropRegion +
+ ", preview crop region = " + previewCropRegion +
+ ", reported zoom ratio = " + reportedZoomRatio);
+ }
+
+ return new ZoomData(bestZoomIndex, reportedCropRegion,
+ previewCropRegion, reportedZoomRatio);
+ }
+
+ /**
* Convert the user-specified crop region into zoom data; which can be used
* to set the parameters to a specific zoom index, or to report back to the user what the
* actual zoom was, or for other calculations requiring the current preview crop region.
@@ -767,15 +858,17 @@ public class ParameterUtils {
final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
previewSize, userCropRegion,
/*out*/reportedCropRegion, /*out*/previewCropRegion);
+ final float reportedZoomRatio = 1.0f;
if (DEBUG) {
Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
"zoomIndex = " + zoomIdx +
", reported crop region = " + reportedCropRegion +
- ", preview crop region = " + previewCropRegion);
+ ", preview crop region = " + previewCropRegion +
+ ", reported zoom ratio = " + reportedZoomRatio);
}
- return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion);
+ return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion, reportedZoomRatio);
}
/**
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index a9f3e04036b5..0c50cb782c24 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -17,7 +17,6 @@
package android.view;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -27,7 +26,6 @@ import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.util.ArraySet;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -396,6 +394,7 @@ public class NotificationHeaderView extends ViewGroup {
addRectAroundView(mIcon);
mExpandButtonRect = addRectAroundView(mExpandButton);
mAppOpsRect = addRectAroundView(mAppOps);
+ setTouchDelegate(new TouchDelegate(mAppOpsRect, mAppOps));
addWidthRect();
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 62dd192a6d67..51d37a53f21f 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5440,6 +5440,9 @@ public class Editor {
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (!mTextView.isFromPrimePointer(ev, true)) {
+ return true;
+ }
if (mFlagInsertionHandleGesturesEnabled && mFlagCursorDragFromAnywhereEnabled) {
// Should only enable touch through when cursor drag is enabled.
// Otherwise the insertion handle view cannot be moved.
@@ -5908,6 +5911,9 @@ public class Editor {
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (!mTextView.isFromPrimePointer(event, true)) {
+ return true;
+ }
boolean superResult = super.onTouchEvent(event);
switch (event.getActionMasked()) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e1783181457f..4be9e1a2051b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -855,6 +855,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int mTextEditSuggestionContainerLayout;
int mTextEditSuggestionHighlightStyle;
+ private static final int NO_POINTER_ID = -1;
+ /**
+ * The prime (the 1st finger) pointer id which is used as a lock to prevent multi touch among
+ * TextView and the handle views which are rendered on popup windows.
+ */
+ private int mPrimePointerId = NO_POINTER_ID;
+
+ /**
+ * Whether the prime pointer is from the event delivered to selection handle or insertion
+ * handle.
+ */
+ private boolean mIsPrimePointerFromHandleView;
+
/**
* {@link EditText} specific data, created on demand when one of the Editor fields is used.
* See {@link #createEditorIfNeeded()}.
@@ -10886,6 +10899,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ /**
+ * Called from onTouchEvent() to prevent the touches by secondary fingers.
+ * Dragging on handles can revise cursor/selection, so can dragging on the text view.
+ * This method is a lock to avoid processing multiple fingers on both text view and handles.
+ * Note: multiple fingers on handles (e.g. 2 fingers on the 2 selection handles) should work.
+ *
+ * @param event The motion event that is being handled and carries the pointer info.
+ * @param fromHandleView true if the event is delivered to selection handle or insertion
+ * handle; false if this event is delivered to TextView.
+ * @return Returns true to indicate that onTouchEvent() can continue processing the motion
+ * event, otherwise false.
+ * - Always returns true for the first finger.
+ * - For secondary fingers, if the first or current finger is from TextView, returns false.
+ * This is to make touch mutually exclusive between the TextView and the handles, but
+ * not among the handles.
+ */
+ boolean isFromPrimePointer(MotionEvent event, boolean fromHandleView) {
+ if (mPrimePointerId == NO_POINTER_ID) {
+ mPrimePointerId = event.getPointerId(0);
+ mIsPrimePointerFromHandleView = fromHandleView;
+ } else if (mPrimePointerId != event.getPointerId(0)) {
+ return mIsPrimePointerFromHandleView && fromHandleView;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_UP
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+ mPrimePointerId = -1;
+ }
+ return true;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (DEBUG_CURSOR) {
@@ -10894,6 +10937,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
MotionEvent.actionToString(event.getActionMasked()),
event.getX(), event.getY());
}
+ if (!isFromPrimePointer(event, false)) {
+ return true;
+ }
final int action = event.getActionMasked();
if (mEditor != null) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index d64b5f1118dc..be66d0c238cc 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -232,9 +232,8 @@ public class AccessibilityShortcutController {
}
/**
- * Show toast if current assigned shortcut target is an accessibility service and its target
- * sdk version is less than or equal to Q, or greater than Q and does not request
- * accessibility button.
+ * Show toast to alert the user that the accessibility shortcut turned on or off an
+ * accessibility service.
*/
private void showToast() {
final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
@@ -247,12 +246,15 @@ public class AccessibilityShortcutController {
}
final boolean requestA11yButton = (serviceInfo.flags
& AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
- if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo
- .targetSdkVersion > Build.VERSION_CODES.Q && requestA11yButton) {
+ final boolean isServiceEnabled = isServiceEnabled(serviceInfo);
+ if (serviceInfo.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
+ > Build.VERSION_CODES.Q && requestA11yButton && isServiceEnabled) {
+ // An accessibility button callback is sent to the target accessibility service.
+ // No need to show up a toast in this case.
return;
}
// For accessibility services, show a toast explaining what we're doing.
- String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+ String toastMessageFormatString = mContext.getString(isServiceEnabled
? R.string.accessibility_shortcut_disabling_service
: R.string.accessibility_shortcut_enabling_service);
String toastMessage = String.format(toastMessageFormatString, serviceName);
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index d43333e507a6..b1e356d258ee 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -300,30 +300,26 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
- UserHandle listUserHandle = activeListAdapter.getUserHandle();
-
- if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
- if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
- UserHandle.myUserId(), listUserHandle.getIdentifier())) {
- if (listUserHandle.equals(mPersonalProfileUserHandle)) {
- DevicePolicyEventLogger.createEvent(
- DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
- .setStrings(getMetricsCategory())
- .write();
- showNoWorkToPersonalIntentsEmptyState(activeListAdapter);
- } else {
- DevicePolicyEventLogger.createEvent(
- DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
- .setStrings(getMetricsCategory())
- .write();
- showNoPersonalToWorkIntentsEmptyState(activeListAdapter);
- }
- return false;
- }
+ if (shouldShowNoCrossProfileIntentsEmptyState(activeListAdapter)) {
+ activeListAdapter.postListReadyRunnable(doPostProcessing);
+ return false;
}
return activeListAdapter.rebuildList(doPostProcessing);
}
+ private boolean shouldShowNoCrossProfileIntentsEmptyState(
+ ResolverListAdapter activeListAdapter) {
+ UserHandle listUserHandle = activeListAdapter.getUserHandle();
+ return UserHandle.myUserId() != listUserHandle.getIdentifier()
+ && allowShowNoCrossProfileIntentsEmptyState()
+ && !mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
+ UserHandle.myUserId(), listUserHandle.getIdentifier());
+ }
+
+ boolean allowShowNoCrossProfileIntentsEmptyState() {
+ return true;
+ }
+
protected abstract void showWorkProfileOffEmptyState(
ResolverListAdapter activeListAdapter, View.OnClickListener listener);
@@ -353,12 +349,35 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
* anyway.
*/
void showEmptyResolverListEmptyState(ResolverListAdapter listAdapter) {
+ if (maybeShowNoCrossProfileIntentsEmptyState(listAdapter)) {
+ return;
+ }
if (maybeShowWorkProfileOffEmptyState(listAdapter)) {
return;
}
maybeShowNoAppsAvailableEmptyState(listAdapter);
}
+ private boolean maybeShowNoCrossProfileIntentsEmptyState(ResolverListAdapter listAdapter) {
+ if (!shouldShowNoCrossProfileIntentsEmptyState(listAdapter)) {
+ return false;
+ }
+ if (listAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
+ .setStrings(getMetricsCategory())
+ .write();
+ showNoWorkToPersonalIntentsEmptyState(listAdapter);
+ } else {
+ DevicePolicyEventLogger.createEvent(
+ DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK)
+ .setStrings(getMetricsCategory())
+ .write();
+ showNoPersonalToWorkIntentsEmptyState(listAdapter);
+ }
+ return true;
+ }
+
/**
* Returns {@code true} if the work profile off empty state screen is shown.
*/
@@ -429,16 +448,16 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
subtitle.setVisibility(View.GONE);
}
- ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
+ button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+ button.setOnClickListener(buttonOnClick);
+
+ ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
if (!getContext().getResources().getBoolean(R.bool.resolver_landscape_phone)) {
icon.setVisibility(View.VISIBLE);
icon.setImageResource(iconRes);
- button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
- button.setOnClickListener(buttonOnClick);
} else {
icon.setVisibility(View.GONE);
- button.setVisibility(View.GONE);
}
activeListAdapter.markTabLoaded();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3e7f24b034ac..d851a099d0e1 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2171,7 +2171,7 @@ public class ChooserActivity extends ResolverActivity implements
mChooserMultiProfilePagerAdapter.getActiveListAdapter();
if (currentListAdapter != null) {
currentListAdapter.updateModel(info.getResolvedComponentName());
- currentListAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
+ currentListAdapter.updateChooserCounts(ri.activityInfo.packageName,
targetIntent.getAction());
}
if (DEBUG) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index dd3a6603f46a..1bc982cdb42b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -179,6 +179,7 @@ public class ResolverActivity extends Activity implements
public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
private BroadcastReceiver mWorkProfileStateReceiver;
+ private boolean mIsHeaderCreated;
/**
* Get the string resource to be used as a label for the link to the resolver activity for an
@@ -479,13 +480,42 @@ public class ResolverActivity extends Activity implements
== workProfileUserHandle.getIdentifier()),
mUseLayoutForBrowsables,
/* userHandle */ workProfileUserHandle);
+ // In the edge case when we have 0 apps in the current profile and >1 apps in the other,
+ // the intent resolver is started in the other profile. Since this is the only case when
+ // this happens, we check for it here and set the current profile's tab.
+ int selectedProfile = getCurrentProfile();
+ UserHandle intentUser = UserHandle.of(getLaunchingUserId());
+ if (!getUser().equals(intentUser)) {
+ if (getPersonalProfileUserHandle().equals(intentUser)) {
+ selectedProfile = PROFILE_PERSONAL;
+ } else if (getWorkProfileUserHandle().equals(intentUser)) {
+ selectedProfile = PROFILE_WORK;
+ }
+ }
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
personalAdapter,
workAdapter,
- /* defaultProfile */ getCurrentProfile(),
+ selectedProfile,
getPersonalProfileUserHandle(),
- getWorkProfileUserHandle());
+ getWorkProfileUserHandle(),
+ /* shouldShowNoCrossProfileIntentsEmptyState= */ getUser().equals(intentUser));
+ }
+
+ /**
+ * Returns the user id of the user that the starting intent originated from.
+ * <p>This is not necessarily equal to {@link #getUserId()} or {@link UserHandle#myUserId()},
+ * as there are edge cases when the intent resolver is launched in the other profile.
+ * For example, when we have 0 resolved apps in current profile and multiple resolved apps
+ * in the other profile, opening a link from the current profile launches the intent resolver
+ * in the other one. b/148536209 for more info.
+ */
+ private int getLaunchingUserId() {
+ int contentUserHint = getIntent().getContentUserHint();
+ if (contentUserHint == UserHandle.USER_CURRENT) {
+ return UserHandle.myUserId();
+ }
+ return contentUserHint;
}
protected @Profile int getCurrentProfile() {
@@ -856,7 +886,7 @@ public class ResolverActivity extends Activity implements
private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
boolean filtered) {
- if (mMultiProfilePagerAdapter.getCurrentUserHandle() != getUser()) {
+ if (!mMultiProfilePagerAdapter.getCurrentUserHandle().equals(getUser())) {
// Never allow the inactive profile to always open an app.
mAlwaysButton.setEnabled(false);
return;
@@ -995,10 +1025,7 @@ public class ResolverActivity extends Activity implements
mMultiProfilePagerAdapter.showListView(listAdapter);
}
if (doPostProcessing) {
- if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
- == UserHandle.myUserId()) {
- setHeader();
- }
+ maybeCreateHeader(listAdapter);
resetButtonBar();
onListRebuilt(listAdapter);
}
@@ -1679,10 +1706,15 @@ public class ResolverActivity extends Activity implements
/**
* Configure the area above the app selection list (title, content preview, etc).
+ * <p>The header is created once when first launching the activity and whenever a package is
+ * installed or uninstalled.
*/
- public void setHeader() {
- if (mMultiProfilePagerAdapter.getActiveListAdapter().getCount() == 0
- && mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0) {
+ private void maybeCreateHeader(ResolverListAdapter listAdapter) {
+ if (mIsHeaderCreated) {
+ return;
+ }
+ if (!shouldShowTabs()
+ && listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
final TextView titleView = findViewById(R.id.title);
if (titleView != null) {
titleView.setVisibility(View.GONE);
@@ -1703,8 +1735,9 @@ public class ResolverActivity extends Activity implements
final ImageView iconView = findViewById(R.id.icon);
if (iconView != null) {
- mMultiProfilePagerAdapter.getActiveListAdapter().loadFilteredItemIconTaskAsync(iconView);
+ listAdapter.loadFilteredItemIconTaskAsync(iconView);
}
+ mIsHeaderCreated = true;
}
protected void resetButtonBar() {
@@ -1804,6 +1837,7 @@ public class ResolverActivity extends Activity implements
// turning on.
return;
}
+ mIsHeaderCreated = false;
boolean listRebuilt = mMultiProfilePagerAdapter.rebuildActiveTab(true);
if (listRebuilt) {
ResolverListAdapter activeListAdapter =
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 579abeecad13..73109c5c1fbc 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -165,8 +165,9 @@ public class ResolverListAdapter extends BaseAdapter {
mResolverListController.updateModel(componentName);
}
- public void updateChooserCounts(String packageName, int userId, String action) {
- mResolverListController.updateChooserCounts(packageName, userId, action);
+ public void updateChooserCounts(String packageName, String action) {
+ mResolverListController.updateChooserCounts(
+ packageName, getUserHandle().getIdentifier(), action);
}
List<ResolvedComponentInfo> getUnfilteredResolveList() {
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index b690a18f2d0e..ad31d8b2e49a 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -35,6 +35,7 @@ import com.android.internal.widget.PagerAdapter;
public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
private final ResolverProfileDescriptor[] mItems;
+ private final boolean mShouldShowNoCrossProfileIntentsEmptyState;
ResolverMultiProfilePagerAdapter(Context context,
ResolverListAdapter adapter,
@@ -44,6 +45,7 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
mItems = new ResolverProfileDescriptor[] {
createProfileDescriptor(adapter)
};
+ mShouldShowNoCrossProfileIntentsEmptyState = true;
}
ResolverMultiProfilePagerAdapter(Context context,
@@ -51,13 +53,15 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
ResolverListAdapter workAdapter,
@Profile int defaultProfile,
UserHandle personalProfileUserHandle,
- UserHandle workProfileUserHandle) {
+ UserHandle workProfileUserHandle,
+ boolean shouldShowNoCrossProfileIntentsEmptyState) {
super(context, /* currentPage */ defaultProfile, personalProfileUserHandle,
workProfileUserHandle);
mItems = new ResolverProfileDescriptor[] {
createProfileDescriptor(personalAdapter),
createProfileDescriptor(workAdapter)
};
+ mShouldShowNoCrossProfileIntentsEmptyState = shouldShowNoCrossProfileIntentsEmptyState;
}
private ResolverProfileDescriptor createProfileDescriptor(
@@ -163,6 +167,11 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
}
@Override
+ boolean allowShowNoCrossProfileIntentsEmptyState() {
+ return mShouldShowNoCrossProfileIntentsEmptyState;
+ }
+
+ @Override
protected void showWorkProfileOffEmptyState(ResolverListAdapter activeListAdapter,
View.OnClickListener listener) {
showEmptyState(activeListAdapter,
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index eda04a6a322a..9bdbdc70d74d 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -16,14 +16,39 @@
package com.android.internal.app.procstats;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_MOD;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_OFF;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_SCREEN_ON;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
import android.os.UserHandle;
import android.service.procstats.ProcessStatsEnums;
import android.service.procstats.ProcessStatsStateProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import static com.android.internal.app.procstats.ProcessStats.*;
-
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -38,6 +63,7 @@ public final class DumpUtils {
public static final String[] STATE_NAMES_CSV;
static final String[] STATE_TAGS;
static final int[] STATE_PROTO_ENUMS;
+ private static final int[] PROCESS_STATS_STATE_TO_AGGREGATED_STATE;
// Make the mapping easy to update.
static {
@@ -126,6 +152,39 @@ public final class DumpUtils {
STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
+
+ // Remap states, as defined by ProcessStats.java, to a reduced subset of states for data
+ // aggregation / size reduction purposes.
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE = new int[STATE_COUNT];
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_PERSISTENT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_PERSISTENT;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_TOP] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_TOP;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_FOREGROUND] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_IMPORTANT_BACKGROUND] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_BACKUP] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ // "Restarting" is not a real state, so this shouldn't exist.
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_SERVICE_RESTARTING] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_UNKNOWN;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_RECEIVER] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_RECEIVER;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HEAVY_WEIGHT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_BACKGROUND;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_HOME] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_LAST_ACTIVITY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_ACTIVITY_CLIENT] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
+ PROCESS_STATS_STATE_TO_AGGREGATED_STATE[STATE_CACHED_EMPTY] =
+ ProcessStatsEnums.AGGREGATED_PROCESS_STATE_CACHED;
}
public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
@@ -455,4 +514,51 @@ public final class DumpUtils {
}
return itemName;
}
+
+ /**
+ * Aggregate process states to reduce size of statistics logs.
+ *
+ * <p>Involves unpacking the three parts of state (process state / device memory state /
+ * screen state), manipulating the elements, then re-packing the new values into a single
+ * int. This integer is guaranteed to be unique for any given combination of state elements.
+ *
+ * @param curState current state as used in mCurState in {@class ProcessState} ie. a value
+ * combined from the process's state, the device's memory pressure state, and
+ * the device's screen on/off state.
+ * @return an integer representing the combination of screen state and process state, where
+ * process state has been aggregated.
+ */
+ public static int aggregateCurrentProcessState(int curState) {
+ int screenStateIndex = curState / (ADJ_SCREEN_MOD * STATE_COUNT);
+ // extract process state from the compound state variable (discarding memory state)
+ int procStateIndex = curState % STATE_COUNT;
+
+ // Remap process state per array above.
+ try {
+ procStateIndex = PROCESS_STATS_STATE_TO_AGGREGATED_STATE[procStateIndex];
+ } catch (IndexOutOfBoundsException e) {
+ procStateIndex = ProcessStatsEnums.AGGREGATED_PROCESS_STATE_UNKNOWN;
+ }
+
+ // Pack screen & process state using bit shifting
+ return (procStateIndex << 0xf) | screenStateIndex;
+ }
+
+ /** Print aggregated tags generated via {@code #aggregateCurrentProcessState}. */
+ public static void printAggregatedProcStateTagProto(ProtoOutputStream proto, long screenId,
+ long stateId, int state) {
+ // screen state is in lowest 0xf bits, process state is in next 0xf bits up
+
+ try {
+ proto.write(stateId, STATE_PROTO_ENUMS[state >> 0xf]);
+ } catch (IndexOutOfBoundsException e) {
+ proto.write(stateId, ProcessStatsEnums.PROCESS_STATE_UNKNOWN);
+ }
+
+ try {
+ proto.write(screenId, ADJ_SCREEN_PROTO_ENUMS[state & 0xf]);
+ } catch (IndexOutOfBoundsException e) {
+ proto.write(screenId, ProcessStatsEnums.SCREEN_STATE_UNKNOWN);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index a6bed5bdfedc..79ff5948f32b 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -16,22 +16,6 @@
package com.android.internal.app.procstats;
-import android.os.Parcel;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.service.procstats.ProcessStatsProto;
-import android.service.procstats.ProcessStatsStateProto;
-import android.util.ArrayMap;
-import android.util.DebugUtils;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.Slog;
-import android.util.SparseLongArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-
-
import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
@@ -60,6 +44,21 @@ import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsStateProto;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.SparseLongArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
+
import com.android.internal.app.procstats.ProcessStats.PackageState;
import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder;
import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
@@ -1418,4 +1417,109 @@ public final class ProcessState {
proto.end(token);
}
+
+ /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
+ public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto, long fieldId,
+ String procName, int uid, long now) {
+ // Group proc stats by aggregated type (only screen state + process state)
+ SparseLongArray durationByState = new SparseLongArray();
+ boolean didCurState = false;
+ for (int i = 0; i < mDurations.getKeyCount(); i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ final int aggregatedType = DumpUtils.aggregateCurrentProcessState(type);
+
+ long time = mDurations.getValue(key);
+ if (mCurCombinedState == type) {
+ didCurState = true;
+ time += now - mStartTime;
+ }
+ int index = durationByState.indexOfKey(aggregatedType);
+ if (index >= 0) {
+ durationByState.put(aggregatedType, time + durationByState.valueAt(index));
+ } else {
+ durationByState.put(aggregatedType, time);
+ }
+ }
+ if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+ final int aggregatedType = DumpUtils.aggregateCurrentProcessState(mCurCombinedState);
+ int index = durationByState.indexOfKey(aggregatedType);
+ if (index >= 0) {
+ durationByState.put(aggregatedType,
+ (now - mStartTime) + durationByState.valueAt(index));
+ } else {
+ durationByState.put(aggregatedType, now - mStartTime);
+ }
+ }
+
+ // Now we have total durations, aggregate the RSS values
+ SparseLongArray meanRssByState = new SparseLongArray();
+ SparseLongArray maxRssByState = new SparseLongArray();
+ // compute weighted averages and max-of-max
+ for (int i = 0; i < mPssTable.getKeyCount(); i++) {
+ final int key = mPssTable.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ if (durationByState.indexOfKey(type) < 0) {
+ // state without duration should not have stats!
+ continue;
+ }
+ final int aggregatedType = DumpUtils.aggregateCurrentProcessState(type);
+
+ long[] rssMeanAndMax = mPssTable.getRssMeanAndMax(key);
+
+ // compute mean * duration, then store sum of that in meanRssByState
+ long meanTimesDuration = rssMeanAndMax[0] * mDurations.getValue(key);
+ if (meanRssByState.indexOfKey(aggregatedType) >= 0) {
+ meanRssByState.put(aggregatedType,
+ meanTimesDuration + meanRssByState.get(aggregatedType));
+ } else {
+ meanRssByState.put(aggregatedType, meanTimesDuration);
+ }
+
+ // accumulate max-of-maxes in maxRssByState
+ if (maxRssByState.indexOfKey(aggregatedType) >= 0
+ && maxRssByState.get(aggregatedType) < rssMeanAndMax[1]) {
+ maxRssByState.put(aggregatedType, rssMeanAndMax[1]);
+ } else if (maxRssByState.indexOfKey(aggregatedType) < 0) {
+ maxRssByState.put(aggregatedType, rssMeanAndMax[1]);
+ }
+ }
+
+ // divide the means by the durations to get the weighted mean-of-means
+ for (int i = 0; i < durationByState.size(); i++) {
+ int aggregatedKey = durationByState.keyAt(i);
+ if (meanRssByState.indexOfKey(aggregatedKey) < 0) {
+ // these data structures should be consistent
+ continue;
+ }
+ meanRssByState.put(aggregatedKey,
+ meanRssByState.get(aggregatedKey) / durationByState.get(aggregatedKey));
+ }
+
+ // build the output
+ final long token = proto.start(fieldId);
+ proto.write(ProcessStatsProto.PROCESS, procName);
+ proto.write(ProcessStatsProto.UID, uid);
+
+ for (int i = 0; i < durationByState.size(); i++) {
+ final long stateToken = proto.start(ProcessStatsProto.STATES);
+
+ final int aggregatedKey = durationByState.keyAt(i);
+
+ DumpUtils.printAggregatedProcStateTagProto(proto,
+ ProcessStatsStateProto.SCREEN_STATE,
+ ProcessStatsStateProto.PROCESS_STATE,
+ aggregatedKey);
+ proto.write(ProcessStatsStateProto.DURATION_MS, durationByState.get(aggregatedKey));
+
+ ProtoUtils.toAggStatsProto(proto, ProcessStatsStateProto.RSS,
+ 0, /* do not output a minimum value */
+ meanRssByState.get(aggregatedKey),
+ maxRssByState.get(aggregatedKey));
+
+ proto.end(stateToken);
+ }
+
+ proto.end(token);
+ }
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 009b1e0fa829..80f6272794d1 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -2178,29 +2178,7 @@ public final class ProcessStats implements Parcelable {
* Writes to ProtoOutputStream.
*/
public void dumpDebug(ProtoOutputStream proto, long now, int section) {
- proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
- proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
- mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
- proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime);
- proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime);
- proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
- proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
- boolean partial = true;
- if ((mFlags & FLAG_SHUTDOWN) != 0) {
- proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
- partial = false;
- }
- if ((mFlags & FLAG_SYSPROPS) != 0) {
- proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
- partial = false;
- }
- if ((mFlags & FLAG_COMPLETE) != 0) {
- proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
- partial = false;
- }
- if (partial) {
- proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
- }
+ dumpProtoPreamble(proto);
final int NPAGETYPES = mPageTypeLabels.size();
for (int i = 0; i < NPAGETYPES; i++) {
@@ -2247,6 +2225,49 @@ public final class ProcessStats implements Parcelable {
}
}
+ /** Similar to {@code #dumpDebug}, but with a reduced/aggregated subset of states. */
+ public void dumpAggregatedProtoForStatsd(ProtoOutputStream proto) {
+ dumpProtoPreamble(proto);
+ final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip = 0; ip < procMap.size(); ip++) {
+ final String procName = procMap.keyAt(ip);
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu = 0; iu < uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final ProcessState procState = uids.valueAt(iu);
+ procState.dumpAggregatedProtoForStatsd(proto,
+ ProcessStatsSectionProto.PROCESS_STATS,
+ procName, uid, mTimePeriodEndRealtime);
+ }
+ }
+ }
+
+ private void dumpProtoPreamble(ProtoOutputStream proto) {
+ proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
+ proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
+ mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+ proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime);
+ proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime);
+ proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
+ proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
+ boolean partial = true;
+ if ((mFlags & FLAG_SHUTDOWN) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
+ partial = false;
+ }
+ if ((mFlags & FLAG_SYSPROPS) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
+ partial = false;
+ }
+ if ((mFlags & FLAG_COMPLETE) != 0) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
+ partial = false;
+ }
+ if (partial) {
+ proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL);
+ }
+ }
+
final public static class ProcessStateHolder {
public final long appVersion;
public ProcessState state;
diff --git a/core/java/com/android/internal/app/procstats/PssTable.java b/core/java/com/android/internal/app/procstats/PssTable.java
index fc93c3a0094e..a6bae6e05dbc 100644
--- a/core/java/com/android/internal/app/procstats/PssTable.java
+++ b/core/java/com/android/internal/app/procstats/PssTable.java
@@ -16,17 +16,17 @@
package com.android.internal.app.procstats;
+import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_AVERAGE;
import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_MAXIMUM;
import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_MINIMUM;
import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
-import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
-import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
import android.service.procstats.ProcessStatsStateProto;
import android.util.proto.ProtoOutputStream;
@@ -133,7 +133,6 @@ public class PssTable extends SparseMappingTable.Table {
}
if (stats[statsIndex + PSS_RSS_MINIMUM] > minRss) {
- stats[statsIndex + PSS_RSS_MINIMUM] = minRss;
}
stats[statsIndex + PSS_RSS_AVERAGE] = (long)(((stats[statsIndex + PSS_RSS_AVERAGE]
@@ -167,4 +166,10 @@ public class PssTable extends SparseMappingTable.Table {
stats[statsIndex + PSS_RSS_AVERAGE],
stats[statsIndex + PSS_RSS_MAXIMUM]);
}
+
+ long[] getRssMeanAndMax(int key) {
+ final long[] stats = getArrayForKey(key);
+ final int statsIndex = SparseMappingTable.getIndexFromKey(key);
+ return new long[]{stats[statsIndex + PSS_RSS_AVERAGE], stats[statsIndex + PSS_RSS_MAXIMUM]};
+ }
}
diff --git a/core/proto/android/service/procstats_enum.proto b/core/proto/android/service/procstats_enum.proto
index cc3fe5af775b..2abf3730aa9f 100644
--- a/core/proto/android/service/procstats_enum.proto
+++ b/core/proto/android/service/procstats_enum.proto
@@ -76,3 +76,27 @@ enum ServiceOperationState {
SERVICE_OPERATION_STATE_BOUND = 4;
SERVICE_OPERATION_STATE_EXECUTING = 5;
}
+
+// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
+// and not frameworks/base/core/java/android/app/ActivityManager.java
+enum AggregatedProcessState {
+ AGGREGATED_PROCESS_STATE_UNKNOWN = 0;
+ // Persistent system process; PERSISTENT or PERSISTENT_UI in ActivityManager
+ AGGREGATED_PROCESS_STATE_PERSISTENT = 1;
+ // Top activity; actually any visible activity; TOP or TOP_SLEEPING in ActivityManager
+ AGGREGATED_PROCESS_STATE_TOP = 2;
+ // Bound top foreground process; BOUND_TOP or BOUND_FOREGROUND_SERVICE in ActivityManager
+ AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+ // Important foreground process; FOREGROUND_SERVICE in ActivityManager
+ AGGREGATED_PROCESS_STATE_FGS = 4;
+ // Important foreground process ; IMPORTANT_FOREGROUND in ActivityManager
+ AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+ // Various background processes; IMPORTANT_BACKGROUND, TRANSIENT_BACKGROUND, BACKUP, SERVICE,
+ // HEAVY_WEIGHT in ActivityManager
+ AGGREGATED_PROCESS_STATE_BACKGROUND = 6;
+ // Process running a receiver; RECEIVER in ActivityManager
+ AGGREGATED_PROCESS_STATE_RECEIVER = 7;
+ // Various cached processes; HOME, LAST_ACTIVITY, CACHED_ACTIVITY, CACHED_RECENT,
+ // CACHED_ACTIVITY_CLIENT, CACHED_EMPTY in ActivityManager
+ AGGREGATED_PROCESS_STATE_CACHED = 8;
+} \ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 6f36aae8a1d4..ece59e2abb22 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -143,34 +143,35 @@
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_marginStart="6dp"
- android:orientation="horizontal" >
- <ImageButton
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:orientation="horizontal">
+ <ImageView
android:id="@+id/camera"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_camera"
- android:background="?android:selectableItemBackgroundBorderless"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_camera_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/mic"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_mic"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_microphone_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/overlay"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_alert_window_layer"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_overlay_active"
/>
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index e986b1886abf..8a57e057b084 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -195,35 +195,36 @@
android:layout_width="wrap_content"
android:paddingTop="3dp"
android:layout_marginStart="2dp"
+ android:background="?android:selectableItemBackgroundBorderless"
android:orientation="horizontal" >
- <ImageButton
+ <ImageView
android:layout_marginStart="4dp"
android:id="@+id/camera"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_camera"
- android:background="?android:selectableItemBackgroundBorderless"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_camera_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/mic"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_mic"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_microphone_active"
/>
- <ImageButton
+ <ImageView
android:id="@+id/overlay"
android:layout_width="?attr/notificationHeaderIconSize"
android:layout_height="?attr/notificationHeaderIconSize"
android:src="@drawable/ic_alert_window_layer"
- android:background="?android:selectableItemBackgroundBorderless"
android:layout_marginStart="4dp"
android:visibility="gone"
+ android:focusable="false"
android:contentDescription="@string/notification_appops_overlay_active"
/>
</LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fba237936f6e..8e99356a8a80 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3447,8 +3447,9 @@
TODO: move to input HAL once ready. -->
<string name="config_doubleTouchGestureEnableFile"></string>
- <!-- Package of the unbundled tv remote service which can connect to tv
- remote provider -->
+ <!-- Comma-separated list of unbundled packages which can connect to the
+ tv remote provider. The tv remote service is an example of such a
+ service. -->
<string name="config_tvRemoteServicePackage" translatable="false"></string>
<!-- True if the device supports persisting security logs across reboots.
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index a9f251e5c3a7..89cc6e743752 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -528,6 +528,47 @@ public class EditorCursorDragTest {
}
@Test
+ public void testCursorDrag_multiTouch() throws Throwable {
+ String text = "line1: This is the 1st line: A";
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Editor editor = tv.getEditorForTesting();
+ final int startIndex = text.indexOf("1st line");
+ Layout layout = tv.getLayout();
+ final float cursorStartX =
+ layout.getPrimaryHorizontal(startIndex) + tv.getTotalPaddingLeft();
+ final float cursorStartY = layout.getLineTop(1) + tv.getTotalPaddingTop();
+
+ // Taps to show the insertion handle.
+ tapAtPoint(tv, cursorStartX, cursorStartY);
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+ View handleView = editor.getInsertionController().getHandle();
+
+ // Taps & holds the insertion handle.
+ long handleDownTime = sTicker.addAndGet(10_000);
+ long eventTime = handleDownTime;
+ dispatchTouchEvent(handleView, downEvent(handleView, handleDownTime, eventTime++, 0, 0));
+
+ // Tries to Drag the cursor, with the pointer id > 0 (meaning the 2nd finger).
+ long cursorDownTime = eventTime++;
+ dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+ tv, MotionEvent.ACTION_DOWN, cursorDownTime, eventTime++, 1,
+ cursorStartX - 50, cursorStartY));
+ dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+ tv, MotionEvent.ACTION_MOVE, cursorDownTime, eventTime++, 1,
+ cursorStartX - 100, cursorStartY));
+ dispatchTouchEvent(tv, obtainTouchEventWithPointerId(
+ tv, MotionEvent.ACTION_UP, cursorDownTime, eventTime++, 1,
+ cursorStartX - 100, cursorStartY));
+
+ // Checks the cursor drag doesn't work while the handle is being hold.
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(startIndex));
+
+ // Finger up on the insertion handle.
+ dispatchTouchEvent(handleView, upEvent(handleView, handleDownTime, eventTime, 0, 0));
+ }
+
+ @Test
public void testCursorDrag_snapDistance() throws Throwable {
String text = "line1: This is the 1st line: A\n"
+ "line2: This is the 2nd line: B\n"
@@ -626,6 +667,24 @@ public class EditorCursorDragTest {
return event;
}
+ private MotionEvent obtainTouchEventWithPointerId(
+ View view, int action, long downTime, long eventTime, int pointerId, float x, float y) {
+ Rect r = new Rect();
+ view.getBoundsOnScreen(r);
+ float rawX = x + r.left;
+ float rawY = y + r.top;
+ MotionEvent.PointerCoords coordinates = new MotionEvent.PointerCoords();
+ coordinates.x = rawX;
+ coordinates.y = rawY;
+ MotionEvent event = MotionEvent.obtain(
+ downTime, eventTime, action, 1, new int[] {pointerId},
+ new MotionEvent.PointerCoords[] {coordinates},
+ 0, 1f, 1f, 0, 0, 0, 0);
+ view.toLocalMotionEvent(event);
+ mMotionEvents.add(event);
+ return event;
+ }
+
private MotionEvent obtainMouseEvent(
View view, int action, long downTime, long eventTime, float x, float y) {
MotionEvent event = obtainTouchEvent(view, action, downTime, eventTime, x, y);
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 4a33da680585..b21504c73772 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -462,6 +462,7 @@ public class AccessibilityShortcutControllerTest {
configureValidShortcutService();
configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
configureRequestAccessibilityButton();
+ configureEnabledService();
Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
getController().performAccessibilityShortcut();
@@ -610,6 +611,11 @@ public class AccessibilityShortcutControllerTest {
}).when(mHandler).sendMessageAtTime(any(), anyLong());
}
+ private void configureEnabledService() throws Exception {
+ when(mAccessibilityManagerService.getEnabledAccessibilityServiceList(anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(mServiceInfo));
+ }
+
private AccessibilityShortcutController getController() {
AccessibilityShortcutController accessibilityShortcutController =
new AccessibilityShortcutController(mContext, mHandler, 0);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index eb39d58019d9..07aa654cae1e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -38,13 +38,16 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertFalse;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.UserHandle;
import android.text.TextUtils;
import android.view.View;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.Espresso;
@@ -543,6 +546,51 @@ public class ResolverActivityTest {
assertThat(activity.getWorkListAdapter().getCount(), is(4));
}
+ @Test
+ public void testWorkTab_headerIsVisibleInPersonalTab() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(1);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createOpenWebsiteIntent();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ TextView headerText = activity.findViewById(R.id.title);
+ String initialText = headerText.getText().toString();
+ assertFalse(initialText.isEmpty(), "Header text is empty.");
+ assertThat(headerText.getVisibility(), is(View.VISIBLE));
+ }
+
+ @Test
+ public void testWorkTab_switchTabs_headerStaysSame() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(1);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createOpenWebsiteIntent();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ TextView headerText = activity.findViewById(R.id.title);
+ String initialText = headerText.getText().toString();
+ onView(withText(R.string.resolver_work_tab))
+ .perform(click());
+
+ waitForIdle();
+ String currentText = headerText.getText().toString();
+ assertThat(headerText.getVisibility(), is(View.VISIBLE));
+ assertThat(String.format("Header text is not the same when switching tabs, personal profile"
+ + " header was %s but work profile header is %s", initialText, currentText),
+ TextUtils.equals(initialText, currentText));
+ }
+
@Ignore // b/148156663
@Test
public void testWorkTab_noPersonalApps_canStartWorkApps()
@@ -761,6 +809,13 @@ public class ResolverActivityTest {
return sendIntent;
}
+ private Intent createOpenWebsiteIntent() {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_VIEW);
+ sendIntent.setData(Uri.parse("https://google.com"));
+ return sendIntent;
+ }
+
private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
for (int i = 0; i < numberOfResults; i++) {
diff --git a/media/OWNERS b/media/OWNERS
index be605831a24b..a16373ef8c8d 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,7 +1,7 @@
andrewlewis@google.com
chz@google.com
-dwkang@google.com
elaurent@google.com
+essick@google.com
etalvala@google.com
gkasten@google.com
hdmoon@google.com
@@ -15,7 +15,7 @@ klhyun@google.com
lajos@google.com
marcone@google.com
sungsoo@google.com
-wjia@google.com
+wonsik@google.com
# For maintaining sync with AndroidX code
per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index a31f177d66ab..362dfa0c88f4 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -132,6 +132,8 @@ static struct {
jmethodID asReadOnlyBufferId;
jmethodID positionId;
jmethodID limitId;
+ jmethodID getPositionId;
+ jmethodID getLimitId;
} gByteBufferInfo;
static struct {
@@ -2033,13 +2035,11 @@ static status_t ConvertKeyValueListsToAMessage(
if (env->IsInstanceOf(jvalue.get(), sFields.mStringClass)) {
const char *tmp = env->GetStringUTFChars((jstring)jvalue.get(), nullptr);
AString value;
- if (tmp) {
- value.setTo(tmp);
- }
- env->ReleaseStringUTFChars((jstring)jvalue.get(), tmp);
- if (value.empty()) {
+ if (!tmp) {
return NO_MEMORY;
}
+ value.setTo(tmp);
+ env->ReleaseStringUTFChars((jstring)jvalue.get(), tmp);
result->setString(key.c_str(), value);
} else if (env->IsInstanceOf(jvalue.get(), sFields.mIntegerClass)) {
jint value = env->CallIntMethod(jvalue.get(), sFields.mIntegerValueId);
@@ -2051,8 +2051,8 @@ static status_t ConvertKeyValueListsToAMessage(
jfloat value = env->CallFloatMethod(jvalue.get(), sFields.mFloatValueId);
result->setFloat(key.c_str(), value);
} else if (env->IsInstanceOf(jvalue.get(), gByteBufferInfo.clazz)) {
- jint position = env->CallIntMethod(jvalue.get(), gByteBufferInfo.positionId);
- jint limit = env->CallIntMethod(jvalue.get(), gByteBufferInfo.limitId);
+ jint position = env->CallIntMethod(jvalue.get(), gByteBufferInfo.getPositionId);
+ jint limit = env->CallIntMethod(jvalue.get(), gByteBufferInfo.getLimitId);
sp<ABuffer> buffer{new ABuffer(limit - position)};
void *data = env->GetDirectBufferAddress(jvalue.get());
if (data != nullptr) {
@@ -2773,6 +2773,14 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) {
clazz.get(), "limit", "(I)Ljava/nio/Buffer;");
CHECK(gByteBufferInfo.limitId != NULL);
+ gByteBufferInfo.getPositionId = env->GetMethodID(
+ clazz.get(), "position", "()I");
+ CHECK(gByteBufferInfo.getPositionId != NULL);
+
+ gByteBufferInfo.getLimitId = env->GetMethodID(
+ clazz.get(), "limit", "()I");
+ CHECK(gByteBufferInfo.getLimitId != NULL);
+
clazz.reset(env->FindClass("java/util/ArrayList"));
CHECK(clazz.get() != NULL);
diff --git a/packages/SystemUI/res/drawable-nodpi/controls_btn_star.xml b/packages/SystemUI/res/drawable-nodpi/controls_btn_star.xml
deleted file mode 100644
index cfe783892b1d..000000000000
--- a/packages/SystemUI/res/drawable-nodpi/controls_btn_star.xml
+++ /dev/null
@@ -1,25 +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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp">
- <item android:state_checked="true"
- android:drawable="@drawable/star_filled"
- android:tint="@color/control_primary_text"/>
- <item android:drawable="@drawable/star_outline"
- android:tint="@color/control_primary_text"/>
-</selector>
diff --git a/packages/SystemUI/res/drawable-nodpi/star_filled.xml b/packages/SystemUI/res/drawable-nodpi/star_filled.xml
deleted file mode 100644
index 62802d3cb838..000000000000
--- a/packages/SystemUI/res/drawable-nodpi/star_filled.xml
+++ /dev/null
@@ -1,27 +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.
- -->
-
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M 11.99 0.027 L 8.628 8.382 L 0.027 9.15 L 6.559 15.111 L 4.597 23.97 L 11.99 19.27 L 19.383 23.97 L 17.421 15.111 L 23.953 9.15 L 15.352 8.382 Z"
- android:fillColor="#FFFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable-nodpi/star_outline.xml b/packages/SystemUI/res/drawable-nodpi/star_outline.xml
deleted file mode 100644
index 13983c6fda8d..000000000000
--- a/packages/SystemUI/res/drawable-nodpi/star_outline.xml
+++ /dev/null
@@ -1,27 +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.
- -->
-
-<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M 11.99 6.491 L 13.15 9.377 L 13.713 10.776 L 15.148 10.902 L 18.103 11.167 L 15.854 13.221 L 14.766 14.216 L 15.089 15.703 L 15.759 18.74 L 13.222 17.127 L 11.99 16.321 L 10.758 17.102 L 8.222 18.715 L 8.891 15.678 L 9.215 14.191 L 8.126 13.196 L 5.877 11.141 L 8.832 10.877 L 10.267 10.751 L 10.83 9.352 L 11.99 6.491 M 11.99 0.027 L 8.628 8.382 L 0.027 9.15 L 6.559 15.111 L 4.597 23.97 L 11.99 19.27 L 19.383 23.97 L 17.421 15.111 L 23.953 9.15 L 15.352 8.382 Z"
- android:fillColor="#FFFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 6a8621398191..fd75d91f0994 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -101,7 +101,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
- android:button="@drawable/controls_btn_star"
android:background="@android:color/transparent"
android:clickable="false"
android:selectable="false"
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 9dc502efab43..f8a96e79d027 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -108,7 +108,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
- style="@style/TextAppearance.NotificationImportanceChannel"/>
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
</LinearLayout>
<TextView
android:id="@+id/delegate_name"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 5b363820e4e2..e8e0133103eb 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -84,7 +84,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
- style="@style/TextAppearance.NotificationImportanceChannel"/>
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
</LinearLayout>
<TextView
android:id="@+id/delegate_name"
diff --git a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
new file mode 100644
index 000000000000..ccb4f7832a62
--- /dev/null
+++ b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml
@@ -0,0 +1,194 @@
+<!--
+ ~ 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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/onboarding_half_shell_container"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ >
+
+ <LinearLayout
+ android:id="@+id/half_shell"
+ android:layout_width="@dimen/qs_panel_width"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="vertical"
+ android:gravity="bottom"
+ android:layout_gravity="center_horizontal|bottom"
+ android:background="@drawable/rounded_bg_full"
+ >
+
+ <!-- We have a known number of rows that can be shown; just design them all here -->
+ <LinearLayout
+ android:id="@+id/show_at_top_tip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:orientation="horizontal"
+ >
+ <ImageView
+ android:id="@+id/bell_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical"
+ android:src="@drawable/ic_notifications_alert"
+ android:tint="?android:attr/colorControlNormal" />
+
+ <TextView
+ android:id="@+id/show_at_top_text"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:textSize="15sp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="@string/priority_onboarding_show_at_top_text"
+ style="@style/TextAppearance.NotificationInfo"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/show_avatar_tip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:orientation="horizontal"
+ >
+ <ImageView
+ android:id="@+id/avatar_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical"
+ android:src="@drawable/ic_person"
+ android:tint="?android:attr/colorControlNormal" />
+
+ <TextView
+ android:id="@+id/avatar_text"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:textSize="15sp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="@string/priority_onboarding_show_avatar_text"
+ style="@style/TextAppearance.NotificationInfo"
+ />
+
+ </LinearLayout>
+
+ <!-- These rows show optionally -->
+
+ <LinearLayout
+ android:id="@+id/floating_bubble_tip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:orientation="horizontal"
+ >
+
+ <ImageView
+ android:id="@+id/bubble_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical"
+ android:src="@drawable/ic_create_bubble"
+ android:tint="?android:attr/colorControlNormal" />
+
+ <TextView
+ android:id="@+id/bubble_text"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:textSize="15sp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="@string/priority_onboarding_appear_as_bubble_text"
+ style="@style/TextAppearance.NotificationInfo"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/ignore_dnd_tip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:orientation="horizontal"
+ >
+
+ <ImageView
+ android:id="@+id/dnd_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical"
+ android:src="@drawable/moon"
+ android:tint="?android:attr/colorControlNormal" />
+
+ <TextView
+ android:id="@+id/dnd_text"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:textSize="15sp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="@string/priority_onboarding_ignores_dnd_text"
+ style="@style/TextAppearance.NotificationInfo"
+ />
+
+ </LinearLayout>
+
+ <!-- Bottom button container -->
+ <RelativeLayout
+ android:id="@+id/button_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:id="@+id/done_button"
+ android:text="@string/priority_onboarding_done_button_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:gravity="end|center_vertical"
+ android:minWidth="@dimen/notification_importance_toggle_size"
+ android:minHeight="@dimen/notification_importance_toggle_size"
+ android:maxWidth="125dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+
+ </RelativeLayout>
+
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
index 716e1272f871..e4b6e0778664 100644
--- a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
+++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
@@ -3,74 +3,9 @@ precision mediump float;
// The actual wallpaper texture.
uniform sampler2D uTexture;
-// The 85th percenile for the luminance histogram of the image (a value between 0 and 1).
-// This value represents the point in histogram that includes 85% of the pixels of the image.
-uniform float uPer85;
-
-// Reveal is the animation value that goes from 1 (the image is hidden) to 0 (the image is visible).
-uniform float uReveal;
-
-// The opacity of locked screen (constant value).
-uniform float uAod2Opacity;
varying vec2 vTextureCoordinates;
-/*
- * Calculates the relative luminance of the pixel.
- */
-vec3 luminosity(vec3 color) {
- float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
- return vec3(lum);
-}
-
-vec4 transform(vec3 diffuse) {
- // Getting the luminance for this pixel
- vec3 lum = luminosity(diffuse);
-
- /*
- * while the reveal > per85, it shows the luminance image (B&W image)
- * then when moving passed that value, the image gets colored.
- */
- float trans = smoothstep(0., uPer85, uReveal);
- diffuse = mix(diffuse, lum, trans);
-
- // 'lower' value represents the capped 'reveal' value to the range [0, per85]
- float selector = step(uPer85, uReveal);
- float lower = mix(uReveal, uPer85, selector);
-
- /*
- * Remaps image:
- * - from reveal=1 to reveal=per85 => lower=per85, diffuse=luminance
- * That means that remaps black and white image pixel
- * from a possible values of [0,1] to [per85, 1] (if the pixel is darker than per85,
- * it's gonna be black, if it's between per85 and 1, it's gonna be gray
- * and if it's 1 it's gonna be white).
- * - from reveal=per85 to reveal=0 => lower=reveal, 'diffuse' changes from luminance to color
- * That means that remaps each image pixel color (rgb)
- * from a possible values of [0,1] to [lower, 1] (if the pixel color is darker than 'lower',
- * it's gonna be 0, if it's between 'lower' and 1, it's gonna be remap to a value
- * between 0 and 1 and if it's 1 it's gonna be 1).
- * - if reveal=0 => lower=0, diffuse=color image
- * The image is shown as it is, colored.
- */
- vec3 remaps = smoothstep(lower, 1., diffuse);
-
- // Interpolate between diffuse and remaps using reveal to avoid over saturation.
- diffuse = mix(diffuse, remaps, uReveal);
-
- /*
- * Fades in the pixel value:
- * - if reveal=1 => fadeInOpacity=0
- * - from reveal=1 to reveal=per85 => 0<=fadeInOpacity<=1
- * - if reveal>per85 => fadeInOpacity=1
- */
- float fadeInOpacity = 1. - smoothstep(uPer85, 1., uReveal);
- diffuse *= uAod2Opacity * fadeInOpacity;
-
- return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
-}
-
void main() {
// gets the pixel value of the wallpaper for this uv coordinates on screen.
- vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
- gl_FragColor = transform(fragColor.rgb);
+ gl_FragColor = texture2D(uTexture, vTextureCoordinates);
} \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 622e4ccef487..599ed1696ec9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1253,6 +1253,7 @@
<dimen name="control_base_item_margin">2dp</dimen>
<dimen name="control_status_padding">3dp</dimen>
<fraction name="controls_toggle_bg_intensity">5%</fraction>
+ <fraction name="controls_dimmed_alpha">40%</fraction>
<!-- Home Controls activity view detail panel-->
<dimen name="controls_activity_view_top_padding">25dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 49420e8620d1..cb20e7a15424 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2639,6 +2639,18 @@
<!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] -->
<string name="inattentive_sleep_warning_title">Standby</string>
+ <!-- Priority conversation onboarding screen -->
+ <!-- Text explaining that priority conversations show at the top of the conversation section [CHAR LIMIT=50] -->
+ <string name="priority_onboarding_show_at_top_text">Show at top of conversation section</string>
+ <!-- Text explaining that priority conversations show an avatar on the lock screen [CHAR LIMIT=50] -->
+ <string name="priority_onboarding_show_avatar_text">Show profile picture on lock screen</string>
+ <!-- Text explaining that priority conversations will appear as a bubble [CHAR LIMIT=50] -->
+ <string name="priority_onboarding_appear_as_bubble_text">Appear as a floating bubble on top of apps</string>
+ <!-- Text explaining that priority conversations can interrupt DnD settings [CHAR LIMIT=50] -->
+ <string name="priority_onboarding_ignores_dnd_text">Interrupt Do Not Disturb</string>
+ <!-- Title for the affirmative button [CHAR LIMIT=50] -->
+ <string name="priority_onboarding_done_button_title">Got it</string>
+
<!-- Window Magnification strings -->
<!-- Title for Magnification Overlay Window [CHAR LIMIT=NONE] -->
<string name="magnification_overlay_title">Magnification Overlay Window</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 5442299881c0..71ec33e16e0e 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -16,9 +16,6 @@
package com.android.systemui;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
import android.os.HandlerThread;
@@ -27,17 +24,12 @@ import android.os.Trace;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.util.Size;
-import android.view.DisplayInfo;
import android.view.SurfaceHolder;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.glwallpaper.EglHelper;
import com.android.systemui.glwallpaper.GLWallpaperRenderer;
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -53,16 +45,12 @@ public class ImageWallpaper extends WallpaperService {
// We delayed destroy render context that subsequent render requests have chance to cancel it.
// This is to avoid destroying then recreating render context in a very short time.
private static final int DELAY_FINISH_RENDERING = 1000;
- private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
- private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
- private static final boolean DEBUG = true;
- private final DozeParameters mDozeParameters;
+ private static final boolean DEBUG = false;
private HandlerThread mWorker;
@Inject
- public ImageWallpaper(DozeParameters dozeParameters) {
+ public ImageWallpaper() {
super();
- mDozeParameters = dozeParameters;
}
@Override
@@ -74,7 +62,7 @@ public class ImageWallpaper extends WallpaperService {
@Override
public Engine onCreateEngine() {
- return new GLEngine(this, mDozeParameters);
+ return new GLEngine();
}
@Override
@@ -84,7 +72,7 @@ public class ImageWallpaper extends WallpaperService {
mWorker = null;
}
- class GLEngine extends Engine implements GLWallpaperRenderer.SurfaceProxy, StateListener {
+ class GLEngine extends Engine {
// Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
// set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
@VisibleForTesting
@@ -94,40 +82,15 @@ public class ImageWallpaper extends WallpaperService {
private GLWallpaperRenderer mRenderer;
private EglHelper mEglHelper;
- private StatusBarStateController mController;
private final Runnable mFinishRenderingTask = this::finishRendering;
- private boolean mShouldStopTransition;
- private final DisplayInfo mDisplayInfo = new DisplayInfo();
- private final Object mMonitor = new Object();
- @VisibleForTesting
- boolean mIsHighEndGfx;
- private boolean mDisplayNeedsBlanking;
- private boolean mNeedTransition;
private boolean mNeedRedraw;
- // This variable can only be accessed in synchronized block.
- private boolean mWaitingForRendering;
- GLEngine(Context context, DozeParameters dozeParameters) {
- init(dozeParameters);
+ GLEngine() {
}
@VisibleForTesting
- GLEngine(DozeParameters dozeParameters, Handler handler) {
+ GLEngine(Handler handler) {
super(SystemClock::elapsedRealtime, handler);
- init(dozeParameters);
- }
-
- private void init(DozeParameters dozeParameters) {
- mIsHighEndGfx = ActivityManager.isHighEndGfx();
- mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
- mNeedTransition = false;
-
- // We will preserve EGL context when we are in lock screen or aod
- // to avoid janking in following transition, we need to release when back to home.
- mController = Dependency.get(StatusBarStateController.class);
- if (mController != null) {
- mController.addCallback(this /* StateListener */);
- }
}
@Override
@@ -135,9 +98,8 @@ public class ImageWallpaper extends WallpaperService {
mEglHelper = getEglHelperInstance();
// Deferred init renderer because we need to get wallpaper by display context.
mRenderer = getRendererInstance();
- getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
setFixedSizeAllowed(true);
- setOffsetNotificationsEnabled(mNeedTransition);
+ setOffsetNotificationsEnabled(false);
updateSurfaceSize();
}
@@ -146,7 +108,7 @@ public class ImageWallpaper extends WallpaperService {
}
ImageWallpaperRenderer getRendererInstance() {
- return new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
+ return new ImageWallpaperRenderer(getDisplayContext());
}
private void updateSurfaceSize() {
@@ -157,79 +119,13 @@ public class ImageWallpaper extends WallpaperService {
holder.setFixedSize(width, height);
}
- /**
- * Check if necessary to stop transition with current wallpaper on this device. <br/>
- * This should only be invoked after {@link #onSurfaceCreated(SurfaceHolder)}}
- * is invoked since it needs display context and surface frame size.
- * @return true if need to stop transition.
- */
- @VisibleForTesting
- boolean checkIfShouldStopTransition() {
- int orientation = getDisplayContext().getResources().getConfiguration().orientation;
- Rect frame = getSurfaceHolder().getSurfaceFrame();
- Rect display = new Rect();
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- display.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
- } else {
- display.set(0, 0, mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
- }
- return mNeedTransition
- && (frame.width() < display.width() || frame.height() < display.height());
- }
-
- @Override
- public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
- float yOffsetStep, int xPixelOffset, int yPixelOffset) {
- if (mWorker == null) return;
- mWorker.getThreadHandler().post(() -> mRenderer.updateOffsets(xOffset, yOffset));
- }
-
- @Override
- public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
- if (mWorker == null || !mNeedTransition) return;
- final long duration = mShouldStopTransition ? 0 : animationDuration;
- if (DEBUG) {
- Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
- + ", duration=" + duration
- + ", mShouldStopTransition=" + mShouldStopTransition);
- }
- mWorker.getThreadHandler().post(
- () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
- if (inAmbientMode && animationDuration == 0) {
- // This means that we are transiting from home to aod, to avoid
- // race condition between window visibility and transition,
- // we don't return until the transition is finished. See b/136643341.
- waitForBackgroundRendering();
- }
- }
-
@Override
public boolean shouldZoomOutWallpaper() {
return true;
}
- private void waitForBackgroundRendering() {
- synchronized (mMonitor) {
- try {
- mWaitingForRendering = true;
- for (int patience = 1; mWaitingForRendering; patience++) {
- mMonitor.wait(INTERVAL_WAIT_FOR_RENDERING);
- mWaitingForRendering &= patience < PATIENCE_WAIT_FOR_RENDERING;
- }
- } catch (InterruptedException ex) {
- } finally {
- mWaitingForRendering = false;
- }
- }
- }
-
@Override
public void onDestroy() {
- if (mController != null) {
- mController.removeCallback(this /* StateListener */);
- }
- mController = null;
-
mWorker.getThreadHandler().post(() -> {
mRenderer.finish();
mRenderer = null;
@@ -240,7 +136,6 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
- mShouldStopTransition = checkIfShouldStopTransition();
if (mWorker == null) return;
mWorker.getThreadHandler().post(() -> {
mEglHelper.init(holder, needSupportWideColorGamut());
@@ -251,32 +146,13 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mWorker == null) return;
- mWorker.getThreadHandler().post(() -> {
- mRenderer.onSurfaceChanged(width, height);
- mNeedRedraw = true;
- });
+ mWorker.getThreadHandler().post(() -> mRenderer.onSurfaceChanged(width, height));
}
@Override
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
if (mWorker == null) return;
- if (DEBUG) {
- Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw);
- }
-
- mWorker.getThreadHandler().post(() -> {
- if (mNeedRedraw) {
- drawFrame();
- mNeedRedraw = false;
- }
- });
- }
-
- @Override
- public void onVisibilityChanged(boolean visible) {
- if (DEBUG) {
- Log.d(TAG, "wallpaper visibility changes: " + visible);
- }
+ mWorker.getThreadHandler().post(this::drawFrame);
}
private void drawFrame() {
@@ -285,15 +161,6 @@ public class ImageWallpaper extends WallpaperService {
postRender();
}
- @Override
- public void onStatePostChange() {
- // When back to home, we try to release EGL, which is preserved in lock screen or aod.
- if (mWorker != null && mController.getState() == StatusBarState.SHADE) {
- mWorker.getThreadHandler().post(this::scheduleFinishRendering);
- }
- }
-
- @Override
public void preRender() {
// This method should only be invoked from worker thread.
Trace.beginSection("ImageWallpaper#preRender");
@@ -330,7 +197,6 @@ public class ImageWallpaper extends WallpaperService {
}
}
- @Override
public void requestRender() {
// This method should only be invoked from worker thread.
Trace.beginSection("ImageWallpaper#requestRender");
@@ -355,27 +221,13 @@ public class ImageWallpaper extends WallpaperService {
}
}
- @Override
public void postRender() {
// This method should only be invoked from worker thread.
Trace.beginSection("ImageWallpaper#postRender");
- notifyWaitingThread();
scheduleFinishRendering();
Trace.endSection();
}
- private void notifyWaitingThread() {
- synchronized (mMonitor) {
- if (mWaitingForRendering) {
- try {
- mWaitingForRendering = false;
- mMonitor.notify();
- } catch (IllegalMonitorStateException ex) {
- }
- }
- }
- }
-
private void cancelFinishRenderingTask() {
if (mWorker == null) return;
mWorker.getThreadHandler().removeCallbacks(mFinishRenderingTask);
@@ -391,18 +243,11 @@ public class ImageWallpaper extends WallpaperService {
Trace.beginSection("ImageWallpaper#finishRendering");
if (mEglHelper != null) {
mEglHelper.destroyEglSurface();
- if (!needPreserveEglContext()) {
- mEglHelper.destroyEglContext();
- }
+ mEglHelper.destroyEglContext();
}
Trace.endSection();
}
- private boolean needPreserveEglContext() {
- return mNeedTransition && mController != null
- && mController.getState() == StatusBarState.KEYGUARD;
- }
-
private boolean needSupportWideColorGamut() {
return mRenderer.isWcgContent();
}
@@ -411,16 +256,6 @@ public class ImageWallpaper extends WallpaperService {
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
super.dump(prefix, fd, out, args);
out.print(prefix); out.print("Engine="); out.println(this);
- out.print(prefix); out.print("isHighEndGfx="); out.println(mIsHighEndGfx);
- out.print(prefix); out.print("displayNeedsBlanking=");
- out.println(mDisplayNeedsBlanking);
- out.print(prefix); out.print("displayInfo="); out.print(mDisplayInfo);
- out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
- out.print(prefix); out.print("mShouldStopTransition=");
- out.println(mShouldStopTransition);
- out.print(prefix); out.print("StatusBarState=");
- out.println(mController != null ? mController.getState() : "null");
-
out.print(prefix); out.print("valid surface=");
out.println(getSurfaceHolder() != null && getSurfaceHolder().getSurface() != null
? getSurfaceHolder().getSurface().isValid()
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 6aa2326c388a..87990cd3bffa 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -21,11 +21,25 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import com.android.systemui.settings.CurrentUserContextTracker;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
import java.util.Set;
+/**
+ * A helper class to store simple preferences for SystemUI. Its main use case is things such as
+ * feature education, e.g. "has the user seen this tooltip".
+ *
+ * As of this writing, feature education settings are *intentionally exempted* from backup and
+ * restore because there is not a great way to know which subset of features the user _should_ see
+ * again if, for instance, they are coming from multiple OSes back or switching OEMs.
+ *
+ * NOTE: Clients of this class should take care to pass in the correct user context when querying
+ * settings, otherwise you will always read/write for user 0 which is almost never what you want.
+ * See {@link CurrentUserContextTracker} for a simple way to get the current context
+ */
public final class Prefs {
private Prefs() {} // no instantation
@@ -109,6 +123,8 @@ public final class Prefs {
String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding";
String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
+ /** Tracks whether the user has seen the onboarding screen for priority conversations */
+ String HAS_SEEN_PRIORITY_ONBOARDING = "HasSeenPriorityOnboarding";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 74b94e76dfc1..319a6e09d4d1 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -25,7 +25,6 @@ import android.os.Looper
import android.os.Message
import android.os.UserHandle
import android.text.TextUtils
-import android.util.Log
import android.util.SparseArray
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
@@ -34,6 +33,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.lang.IllegalStateException
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
@@ -189,8 +189,8 @@ open class BroadcastDispatcher @Inject constructor (
data.user.identifier
}
if (userId < UserHandle.USER_ALL) {
- if (DEBUG) Log.w(TAG, "Register receiver for invalid user: $userId")
- return
+ throw IllegalStateException(
+ "Attempting to register receiver for invalid user {$userId}")
}
val uBR = receiversByUser.get(userId, createUBRForUser(userId))
receiversByUser.put(userId, uBR)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1cabe221bef1..0aabdff96e1e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -374,8 +374,9 @@ public class BubbleStackView extends FrameLayout {
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mExpandedAnimationController.dismissDraggedOutBubble(
- mExpandedAnimationController.getDraggedOutBubble(),
- BubbleStackView.this::dismissMagnetizedObject);
+ mExpandedAnimationController.getDraggedOutBubble() /* bubble */,
+ mDismissTargetContainer.getHeight() /* translationYBy */,
+ BubbleStackView.this::dismissMagnetizedObject /* after */);
hideDismissTarget();
}
};
@@ -405,7 +406,8 @@ public class BubbleStackView extends FrameLayout {
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mStackAnimationController.implodeStack(
+ mStackAnimationController.animateStackDismissal(
+ mDismissTargetContainer.getHeight() /* translationYBy */,
() -> {
resetDesaturationAndDarken();
dismissMagnetizedObject();
@@ -954,6 +956,8 @@ public class BubbleStackView extends FrameLayout {
mVerticalPosPercentBeforeRotation =
(mStackAnimationController.getStackPosition().y - allowablePos.top)
/ (allowablePos.bottom - allowablePos.top);
+ mVerticalPosPercentBeforeRotation =
+ Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation));
addOnLayoutChangeListener(mOrientationChangedListener);
hideFlyoutImmediate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index d974adc34ee0..0002e862bb41 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -329,7 +329,7 @@ public class ExpandedAnimationController
}
/** Plays a dismiss animation on the dragged out bubble. */
- public void dismissDraggedOutBubble(View bubble, Runnable after) {
+ public void dismissDraggedOutBubble(View bubble, float translationYBy, Runnable after) {
if (bubble == null) {
return;
}
@@ -337,6 +337,7 @@ public class ExpandedAnimationController
.withStiffness(SpringForce.STIFFNESS_HIGH)
.scaleX(1.1f)
.scaleY(1.1f)
+ .translationY(bubble.getTranslationY() + translationYBy)
.alpha(0f, after)
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 00de8b4a51b8..5f3a2bd9eb8b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -647,17 +647,18 @@ public class StackAnimationController extends
}
/**
- * 'Implode' the stack by shrinking the bubbles via chained animations and fading them out.
+ * 'Implode' the stack by shrinking the bubbles, fading them out, and translating them down.
*/
- public void implodeStack(Runnable after) {
- // Pop and fade the bubbles sequentially.
- animationForChildAtIndex(0)
- .scaleX(0.5f)
- .scaleY(0.5f)
- .alpha(0f)
- .withDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
- .withStiffness(SpringForce.STIFFNESS_HIGH)
- .start(after);
+ public void animateStackDismissal(float translationYBy, Runnable after) {
+ animationsForChildrenFromIndex(0, (index, animation) ->
+ animation
+ .scaleX(0.5f)
+ .scaleY(0.5f)
+ .alpha(0f)
+ .translationY(
+ mLayout.getChildAt(index).getTranslationY() + translationYBy)
+ .withStiffness(SpringForce.STIFFNESS_HIGH))
+ .startAll(after);
}
/**
@@ -710,8 +711,6 @@ public class StackAnimationController extends
if (property.equals(DynamicAnimation.TRANSLATION_X)
|| property.equals(DynamicAnimation.TRANSLATION_Y)) {
return index + 1;
- } else if (isStackStuckToTarget()) {
- return index + 1; // Chain all animations in dismiss (scale, alpha, etc. are used).
} else {
return NONE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 7cab847d52f7..79a5b0a1ce52 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -23,6 +23,7 @@ import android.service.controls.actions.ControlAction
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.UserAwareController
import com.android.systemui.controls.management.ControlsFavoritingActivity
+import com.android.systemui.controls.ui.ControlWithState
import com.android.systemui.controls.ui.ControlsUiController
import java.util.function.Consumer
@@ -111,6 +112,13 @@ interface ControlsController : UserAwareController {
@ControlAction.ResponseResult response: Int
)
+ /**
+ * When a control should be highlighted, dimming down what's around it.
+ *
+ * @param cws focused control, or {@code null} if nothing should be highlighted.
+ */
+ fun onFocusChanged(cws: ControlWithState?)
+
// FAVORITE MANAGEMENT
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 6d34009169d5..5626a5de2e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -41,6 +41,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.ui.ControlWithState
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
@@ -504,6 +505,10 @@ class ControlsControllerImpl @Inject constructor (
}
}
+ override fun onFocusChanged(cws: ControlWithState?) {
+ uiController.onFocusChanged(cws)
+ }
+
override fun refreshStatus(componentName: ComponentName, control: Control) {
if (!confirmAvailability()) {
Log.d(TAG, "Controls not available")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index 2c1a91dca225..b3c6cab2adff 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -25,6 +25,7 @@ import android.service.controls.actions.CommandAction
import android.util.Log
import android.view.HapticFeedbackConstants
import com.android.systemui.R
+import com.android.systemui.controls.controller.ControlsController
object ControlActionCoordinator {
const val MIN_LEVEL = 0
@@ -76,4 +77,8 @@ object ControlActionCoordinator {
it.show()
}
}
+
+ fun setFocusedElement(cvh: ControlViewHolder?, controlsController: ControlsController) {
+ controlsController.onFocusChanged(cvh?.cws)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 93e1bd444938..61a323d0b9a8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -31,6 +31,7 @@ import android.service.controls.templates.StatelessTemplate
import android.service.controls.templates.TemperatureControlTemplate
import android.service.controls.templates.ToggleRangeTemplate
import android.service.controls.templates.ToggleTemplate
+import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
@@ -68,6 +69,8 @@ class ControlViewHolder(
private val toggleBackgroundIntensity: Float = layout.context.resources
.getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)
+ private val dimmedAlpha: Float = layout.context.resources
+ .getFraction(R.fraction.controls_dimmed_alpha, 1, 1)
private var stateAnimator: ValueAnimator? = null
private val baseLayer: GradientDrawable
val icon: ImageView = layout.requireViewById(R.id.icon)
@@ -82,6 +85,11 @@ class ControlViewHolder(
var lastAction: ControlAction? = null
val deviceType: Int
get() = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType
+ var dimmed: Boolean = false
+ set(value) {
+ field = value
+ bindData(cws)
+ }
init {
val ld = layout.getBackground() as LayerDrawable
@@ -177,7 +185,8 @@ class ControlViewHolder(
val fg = context.resources.getColorStateList(ri.foreground, context.theme)
val bg = context.resources.getColor(R.color.control_default_background, context.theme)
- val (clip, newAlpha) = if (enabled) {
+ val dimAlpha = if (dimmed) dimmedAlpha else 1f
+ var (clip, newAlpha) = if (enabled) {
listOf(ri.enabledBackground, ALPHA_ENABLED)
} else {
listOf(R.color.control_default_background, ALPHA_DISABLED)
@@ -202,12 +211,14 @@ class ControlViewHolder(
if (animated) {
val oldColor = color?.defaultColor ?: newClipColor
val oldBaseColor = baseLayer.color?.defaultColor ?: newBaseColor
+ val oldAlpha = layout.alpha
stateAnimator = ValueAnimator.ofInt(clipLayer.alpha, newAlpha).apply {
addUpdateListener {
alpha = it.animatedValue as Int
setColor(ColorUtils.blendARGB(oldColor, newClipColor, it.animatedFraction))
baseLayer.setColor(ColorUtils.blendARGB(oldBaseColor,
newBaseColor, it.animatedFraction))
+ layout.alpha = MathUtils.lerp(oldAlpha, dimAlpha, it.animatedFraction)
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
@@ -222,6 +233,7 @@ class ControlViewHolder(
alpha = newAlpha
setColor(newClipColor)
baseLayer.setColor(newBaseColor)
+ layout.alpha = dimAlpha
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 0f105376847f..61a1a986c091 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -36,4 +36,5 @@ interface ControlsUiController {
controlId: String,
@ControlAction.ResponseResult response: Int
)
+ fun onFocusChanged(controlWithState: ControlWithState?)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index fab6fc7357dd..2adfb1bd6d4f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -209,6 +209,20 @@ class ControlsUiControllerImpl @Inject constructor (
}
}
+ override fun onFocusChanged(focusedControl: ControlWithState?) {
+ controlViewsById.forEach { key: ControlKey, viewHolder: ControlViewHolder ->
+ val state = controlsById.get(key) ?: return@forEach
+ val shouldBeDimmed = focusedControl != null && state != focusedControl
+ if (viewHolder.dimmed == shouldBeDimmed) {
+ return@forEach
+ }
+
+ uiExecutor.execute {
+ viewHolder.dimmed = shouldBeDimmed
+ }
+ }
+ }
+
private fun startFavoritingActivity(context: Context, si: StructureInfo) {
startTargetedActivity(context, si, ControlsFavoritingActivity::class.java)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index d8b26e2e68d8..dafd8b25ec2e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -175,6 +175,7 @@ class ToggleRangeBehavior : Behavior {
fun beginUpdateRange() {
status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
.getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
+ ControlActionCoordinator.setFocusedElement(cvh, cvh.controlsController)
}
fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) {
@@ -243,6 +244,7 @@ class ToggleRangeBehavior : Behavior {
status.setText("$currentStatusText $currentRangeValue")
cvh.action(FloatAction(rangeTemplate.getTemplateId(),
findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
+ ControlActionCoordinator.setFocusedElement(null, cvh.controlsController)
}
fun findNearestStep(value: Float): Float {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 8c572fe8f842..88f96a8b19fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -24,6 +24,7 @@ import android.content.Context;
import androidx.annotation.Nullable;
import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.plugins.qs.QSFactory;
@@ -33,6 +34,7 @@ import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.settings.CurrentUserContextTracker;
import com.android.systemui.stackdivider.DividerModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -136,4 +138,15 @@ public abstract class SystemUIDefaultModule {
@Binds
abstract KeyguardViewController bindKeyguardViewController(
StatusBarKeyguardViewManager statusBarKeyguardViewManager);
+
+ @Singleton
+ @Provides
+ static CurrentUserContextTracker provideCurrentUserContextTracker(
+ Context context,
+ BroadcastDispatcher broadcastDispatcher) {
+ CurrentUserContextTracker tracker =
+ new CurrentUserContextTracker(context, broadcastDispatcher);
+ tracker.initialize();
+ return tracker;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 2c080b8efc63..9bb253b3d890 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -395,6 +395,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
if (preferredComponent == null) {
Log.i(TAG, "Controls seeding: No preferred component has been set, will not seed");
mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, true).apply();
+ return;
}
mControlsController.seedFavoritesForComponent(
@@ -2316,4 +2317,4 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
&& mControlsUiController.getAvailable()
&& !mControlsServiceInfos.isEmpty();
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
index 88ab9ef4b014..61524900b89b 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/GLWallpaperRenderer.java
@@ -49,20 +49,6 @@ public interface GLWallpaperRenderer {
void onDrawFrame();
/**
- * Notify ambient mode is changed.
- * @param inAmbientMode true if in ambient mode.
- * @param duration duration of transition.
- */
- void updateAmbientMode(boolean inAmbientMode, long duration);
-
- /**
- * Notify the wallpaper offsets changed.
- * @param xOffset offset along x axis.
- * @param yOffset offset along y axis.
- */
- void updateOffsets(float xOffset, float yOffset);
-
- /**
* Ask renderer to report the surface size it needs.
*/
Size reportSurfaceSize();
@@ -81,24 +67,4 @@ public interface GLWallpaperRenderer {
*/
void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args);
- /**
- * A proxy which owns surface holder.
- */
- interface SurfaceProxy {
-
- /**
- * Ask proxy to start rendering frame to surface.
- */
- void requestRender();
-
- /**
- * Ask proxy to prepare render context.
- */
- void preRender();
-
- /**
- * Ask proxy to destroy render context.
- */
- void postRender();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
index 626d0cfed997..fa45ea1acb95 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -33,7 +33,6 @@ import static android.opengl.GLES20.glUniform1i;
import static android.opengl.GLES20.glVertexAttribPointer;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.opengl.GLUtils;
import android.util.Log;
@@ -50,14 +49,9 @@ import java.nio.FloatBuffer;
class ImageGLWallpaper {
private static final String TAG = ImageGLWallpaper.class.getSimpleName();
- static final String A_POSITION = "aPosition";
- static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
- static final String U_PER85 = "uPer85";
- static final String U_REVEAL = "uReveal";
- static final String U_AOD2OPACITY = "uAod2Opacity";
- static final String U_TEXTURE = "uTexture";
-
- private static final int HANDLE_UNDEFINED = -1;
+ private static final String A_POSITION = "aPosition";
+ private static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
+ private static final String U_TEXTURE = "uTexture";
private static final int POSITION_COMPONENT_COUNT = 2;
private static final int TEXTURE_COMPONENT_COUNT = 2;
private static final int BYTES_PER_FLOAT = 4;
@@ -88,14 +82,9 @@ class ImageGLWallpaper {
private int mAttrPosition;
private int mAttrTextureCoordinates;
- private int mUniAod2Opacity;
- private int mUniPer85;
- private int mUniReveal;
private int mUniTexture;
private int mTextureId;
- private float[] mCurrentTexCoordinate;
-
ImageGLWallpaper(ImageGLProgram program) {
mProgram = program;
@@ -135,31 +124,9 @@ class ImageGLWallpaper {
}
private void setupUniforms() {
- mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
- mUniPer85 = mProgram.getUniformHandle(U_PER85);
- mUniReveal = mProgram.getUniformHandle(U_REVEAL);
mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
}
- int getHandle(String name) {
- switch (name) {
- case A_POSITION:
- return mAttrPosition;
- case A_TEXTURE_COORDINATES:
- return mAttrTextureCoordinates;
- case U_AOD2OPACITY:
- return mUniAod2Opacity;
- case U_PER85:
- return mUniPer85;
- case U_REVEAL:
- return mUniReveal;
- case U_TEXTURE:
- return mUniTexture;
- default:
- return HANDLE_UNDEFINED;
- }
- }
-
void draw() {
glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
}
@@ -201,87 +168,6 @@ class ImageGLWallpaper {
}
/**
- * This method adjust s(x-axis), t(y-axis) texture coordinates to get current display area
- * of texture and will be used during transition.
- * The adjustment happens if either the width or height of the surface is larger than
- * corresponding size of the display area.
- * If both width and height are larger than corresponding size of the display area,
- * the adjustment will happen at both s, t side.
- *
- * @param surface The size of the surface.
- * @param scissor The display area.
- * @param xOffset The offset amount along s axis.
- * @param yOffset The offset amount along t axis.
- */
- void adjustTextureCoordinates(Rect surface, Rect scissor, float xOffset, float yOffset) {
- mCurrentTexCoordinate = TEXTURES.clone();
-
- if (surface == null || scissor == null) {
- mTextureBuffer.put(mCurrentTexCoordinate);
- mTextureBuffer.position(0);
- return;
- }
-
- int surfaceWidth = surface.width();
- int surfaceHeight = surface.height();
- int scissorWidth = scissor.width();
- int scissorHeight = scissor.height();
-
- if (surfaceWidth > scissorWidth) {
- // Calculate the new s pos in pixels.
- float pixelS = (float) Math.round((surfaceWidth - scissorWidth) * xOffset);
- // Calculate the s pos in texture coordinate.
- float coordinateS = pixelS / surfaceWidth;
- // Calculate the percentage occupied by the scissor width in surface width.
- float surfacePercentageW = (float) scissorWidth / surfaceWidth;
- // Need also consider the case if surface height is smaller than scissor height.
- if (surfaceHeight < scissorHeight) {
- // We will narrow the surface percentage to keep aspect ratio.
- surfacePercentageW *= (float) surfaceHeight / scissorHeight;
- }
- // Determine the final s pos, also limit the legal s pos to prevent from out of range.
- float s = coordinateS + surfacePercentageW > 1f ? 1f - surfacePercentageW : coordinateS;
- // Traverse the s pos in texture coordinates array and adjust the s pos accordingly.
- for (int i = 0; i < mCurrentTexCoordinate.length; i += 2) {
- // indices 2, 4 and 6 are the end of s coordinates.
- if (i == 2 || i == 4 || i == 6) {
- mCurrentTexCoordinate[i] = Math.min(1f, s + surfacePercentageW);
- } else {
- mCurrentTexCoordinate[i] = s;
- }
- }
- }
-
- if (surfaceHeight > scissorHeight) {
- // Calculate the new t pos in pixels.
- float pixelT = (float) Math.round((surfaceHeight - scissorHeight) * yOffset);
- // Calculate the t pos in texture coordinate.
- float coordinateT = pixelT / surfaceHeight;
- // Calculate the percentage occupied by the scissor height in surface height.
- float surfacePercentageH = (float) scissorHeight / surfaceHeight;
- // Need also consider the case if surface width is smaller than scissor width.
- if (surfaceWidth < scissorWidth) {
- // We will narrow the surface percentage to keep aspect ratio.
- surfacePercentageH *= (float) surfaceWidth / scissorWidth;
- }
- // Determine the final t pos, also limit the legal t pos to prevent from out of range.
- float t = coordinateT + surfacePercentageH > 1f ? 1f - surfacePercentageH : coordinateT;
- // Traverse the t pos in texture coordinates array and adjust the t pos accordingly.
- for (int i = 1; i < mCurrentTexCoordinate.length; i += 2) {
- // indices 1, 3 and 11 are the end of t coordinates.
- if (i == 1 || i == 3 || i == 11) {
- mCurrentTexCoordinate[i] = Math.min(1f, t + surfacePercentageH);
- } else {
- mCurrentTexCoordinate[i] = t;
- }
- }
- }
-
- mTextureBuffer.put(mCurrentTexCoordinate);
- mTextureBuffer.position(0);
- }
-
- /**
* Called to dump current state.
* @param prefix prefix.
* @param fd fd.
@@ -289,17 +175,5 @@ class ImageGLWallpaper {
* @param args args.
*/
public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
- StringBuilder sb = new StringBuilder();
- sb.append('{');
- if (mCurrentTexCoordinate != null) {
- for (int i = 0; i < mCurrentTexCoordinate.length; i++) {
- sb.append(mCurrentTexCoordinate[i]).append(',');
- if (i == mCurrentTexCoordinate.length - 1) {
- sb.deleteCharAt(sb.length() - 1);
- }
- }
- }
- sb.append('}');
- out.print(prefix); out.print("mTexCoordinates="); out.println(sb.toString());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
deleted file mode 100644
index 703d5910500a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
+++ /dev/null
@@ -1,246 +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.
- */
-
-package com.android.systemui.glwallpaper;
-
-import static com.android.systemui.glwallpaper.ImageWallpaperRenderer.WallpaperTexture;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Handler.Callback;
-import android.os.Message;
-import android.util.Log;
-
-/**
- * A helper class that computes threshold from a bitmap.
- * Threshold will be computed each time the user picks a new image wallpaper.
- */
-class ImageProcessHelper {
- private static final String TAG = ImageProcessHelper.class.getSimpleName();
- private static final float DEFAULT_THRESHOLD = 0.8f;
- private static final float DEFAULT_OTSU_THRESHOLD = 0f;
- private static final float MAX_THRESHOLD = 0.89f;
- private static final int MSG_UPDATE_THRESHOLD = 1;
-
- /**
- * This color matrix will be applied to each pixel to get luminance from rgb by below formula:
- * Luminance = .2126f * r + .7152f * g + .0722f * b.
- */
- private static final float[] LUMINOSITY_MATRIX = new float[] {
- .2126f, .0000f, .0000f, .0000f, .0000f,
- .0000f, .7152f, .0000f, .0000f, .0000f,
- .0000f, .0000f, .0722f, .0000f, .0000f,
- .0000f, .0000f, .0000f, 1.000f, .0000f
- };
-
- private final Handler mHandler = new Handler(new Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_THRESHOLD:
- mThreshold = (float) msg.obj;
- return true;
- default:
- return false;
- }
- }
- });
-
- private float mThreshold = DEFAULT_THRESHOLD;
-
- void start(WallpaperTexture texture) {
- new ThresholdComputeTask(mHandler).execute(texture);
- }
-
- float getThreshold() {
- return Math.min(mThreshold, MAX_THRESHOLD);
- }
-
- private static class ThresholdComputeTask extends AsyncTask<WallpaperTexture, Void, Float> {
- private Handler mUpdateHandler;
-
- ThresholdComputeTask(Handler handler) {
- super(handler);
- mUpdateHandler = handler;
- }
-
- @Override
- protected Float doInBackground(WallpaperTexture... textures) {
- WallpaperTexture texture = textures[0];
- final float[] threshold = new float[] {DEFAULT_THRESHOLD};
- if (texture == null) {
- Log.e(TAG, "ThresholdComputeTask: WallpaperTexture not initialized");
- return threshold[0];
- }
-
- texture.use(bitmap -> {
- if (bitmap != null) {
- threshold[0] = new Threshold().compute(bitmap);
- } else {
- Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
- }
- });
- return threshold[0];
- }
-
- @Override
- protected void onPostExecute(Float result) {
- Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result);
- mUpdateHandler.sendMessage(msg);
- }
- }
-
- private static class Threshold {
- public float compute(Bitmap bitmap) {
- Bitmap grayscale = toGrayscale(bitmap);
- int[] histogram = getHistogram(grayscale);
- boolean isSolidColor = isSolidColor(grayscale, histogram);
-
- // We will see gray wallpaper during the transition if solid color wallpaper is set,
- // please refer to b/130360362#comment16.
- // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set.
- ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus();
- return algorithm.compute(grayscale, histogram);
- }
-
- private Bitmap toGrayscale(Bitmap bitmap) {
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
-
- Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig(),
- false /* hasAlpha */, bitmap.getColorSpace());
- Canvas canvas = new Canvas(grayscale);
- ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
- Paint paint = new Paint();
- paint.setColorFilter(new ColorMatrixColorFilter(cm));
- canvas.drawBitmap(bitmap, new Matrix(), paint);
-
- return grayscale;
- }
-
- private int[] getHistogram(Bitmap grayscale) {
- int width = grayscale.getWidth();
- int height = grayscale.getHeight();
-
- // TODO: Fine tune the performance here, tracking on b/123615079.
- int[] histogram = new int[256];
- for (int row = 0; row < height; row++) {
- for (int col = 0; col < width; col++) {
- int pixel = grayscale.getPixel(col, row);
- int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
- histogram[y]++;
- }
- }
-
- return histogram;
- }
-
- private boolean isSolidColor(Bitmap bitmap, int[] histogram) {
- boolean solidColor = false;
- int pixels = bitmap.getWidth() * bitmap.getHeight();
-
- // In solid color case, only one element of histogram has value,
- // which is pixel counts and the value of other elements should be 0.
- for (int value : histogram) {
- if (value != 0 && value != pixels) {
- break;
- }
- if (value == pixels) {
- solidColor = true;
- break;
- }
- }
- return solidColor;
- }
- }
-
- private static class Percentile85 implements ThresholdAlgorithm {
- @Override
- public float compute(Bitmap bitmap, int[] histogram) {
- float per85 = DEFAULT_THRESHOLD;
- int pixelCount = bitmap.getWidth() * bitmap.getHeight();
- float[] acc = new float[256];
- for (int i = 0; i < acc.length; i++) {
- acc[i] = (float) histogram[i] / pixelCount;
- float prev = i == 0 ? 0f : acc[i - 1];
- float next = acc[i];
- float idx = (float) (i + 1) / 255;
- float sum = prev + next;
- if (prev < 0.85f && sum >= 0.85f) {
- per85 = idx;
- }
- if (i > 0) {
- acc[i] += acc[i - 1];
- }
- }
- return per85;
- }
- }
-
- private static class Otsus implements ThresholdAlgorithm {
- @Override
- public float compute(Bitmap bitmap, int[] histogram) {
- float threshold = DEFAULT_OTSU_THRESHOLD;
- float maxVariance = 0;
- float pixelCount = bitmap.getWidth() * bitmap.getHeight();
- float[] w = new float[2];
- float[] m = new float[2];
- float[] u = new float[2];
-
- for (int i = 0; i < histogram.length; i++) {
- m[1] += i * histogram[i];
- }
-
- w[1] = pixelCount;
- for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) {
- float dU;
- float variance;
- float numPixels = histogram[tonalValue];
- float tmp = numPixels * tonalValue;
- w[0] += numPixels;
- w[1] -= numPixels;
-
- if (w[0] == 0 || w[1] == 0) {
- continue;
- }
-
- m[0] += tmp;
- m[1] -= tmp;
- u[0] = m[0] / w[0];
- u[1] = m[1] / w[1];
- dU = u[0] - u[1];
- variance = w[0] * w[1] * dU * dU;
-
- if (variance > maxVariance) {
- threshold = (tonalValue + 1f) / histogram.length;
- maxVariance = variance;
- }
- }
- return threshold;
- }
- }
-
- private interface ThresholdAlgorithm {
- float compute(Bitmap bitmap, int[] histogram);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
deleted file mode 100644
index f815b5d476ec..000000000000
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ /dev/null
@@ -1,126 +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.
- */
-
-package com.android.systemui.glwallpaper;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.util.Log;
-
-import com.android.systemui.Interpolators;
-
-/**
- * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition.
- * The transition will happen while getting awake and quit events.
- */
-class ImageRevealHelper {
- private static final String TAG = ImageRevealHelper.class.getSimpleName();
- private static final float MAX_REVEAL = 0f;
- private static final float MIN_REVEAL = 1f;
- private static final boolean DEBUG = true;
-
- private final ValueAnimator mAnimator;
- private final RevealStateListener mRevealListener;
- private float mReveal = MAX_REVEAL;
- private boolean mAwake = false;
-
- ImageRevealHelper(RevealStateListener listener) {
- mRevealListener = listener;
- mAnimator = ValueAnimator.ofFloat();
- mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mAnimator.addUpdateListener(animator -> {
- mReveal = (float) animator.getAnimatedValue();
- if (mRevealListener != null) {
- mRevealListener.onRevealStateChanged();
- }
- });
- mAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mIsCanceled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mIsCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mIsCanceled && mRevealListener != null) {
- if (DEBUG) {
- Log.d(TAG, "transition end");
- }
- mRevealListener.onRevealEnd();
- }
- mIsCanceled = false;
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- if (mRevealListener != null) {
- if (DEBUG) {
- Log.d(TAG, "transition start");
- }
- mRevealListener.onRevealStart(true /* animate */);
- }
- }
- });
- }
-
- public float getReveal() {
- return mReveal;
- }
-
- void updateAwake(boolean awake, long duration) {
- if (DEBUG) {
- Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
- }
- mAnimator.cancel();
- mAwake = awake;
- if (duration == 0) {
- // We are transiting from home to aod or aod to home directly,
- // we don't need to do transition in these cases.
- mReveal = mAwake ? MAX_REVEAL : MIN_REVEAL;
- mRevealListener.onRevealStart(false /* animate */);
- mRevealListener.onRevealStateChanged();
- mRevealListener.onRevealEnd();
- } else {
- mAnimator.setDuration(duration);
- mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
- mAnimator.start();
- }
- }
-
- /**
- * A listener to trace value changes of reveal.
- */
- public interface RevealStateListener {
-
- /**
- * Called back while reveal status changes.
- */
- void onRevealStateChanged();
-
- /**
- * Called back while reveal starts.
- */
- void onRevealStart(boolean animate);
-
- /**
- * Called back while reveal ends.
- */
- void onRevealEnd();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index e9ddb3831b1a..1a0356c4446d 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -19,18 +19,14 @@ package com.android.systemui.glwallpaper;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
-import static android.opengl.GLES20.glUniform1f;
import static android.opengl.GLES20.glViewport;
import android.app.WallpaperManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.util.Log;
-import android.util.MathUtils;
import android.util.Size;
-import android.view.DisplayInfo;
import com.android.systemui.R;
@@ -42,57 +38,24 @@ import java.util.function.Consumer;
/**
* A GL renderer for image wallpaper.
*/
-public class ImageWallpaperRenderer implements GLWallpaperRenderer,
- ImageRevealHelper.RevealStateListener {
+public class ImageWallpaperRenderer implements GLWallpaperRenderer {
private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
- private static final float SCALE_VIEWPORT_MIN = 1f;
- private static final float SCALE_VIEWPORT_MAX = 1.1f;
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private final ImageGLProgram mProgram;
private final ImageGLWallpaper mWallpaper;
- private final ImageProcessHelper mImageProcessHelper;
- private final ImageRevealHelper mImageRevealHelper;
-
- private SurfaceProxy mProxy;
- private final Rect mScissor;
private final Rect mSurfaceSize = new Rect();
- private final Rect mViewport = new Rect();
- private boolean mScissorMode;
- private float mXOffset;
- private float mYOffset;
private final WallpaperTexture mTexture;
- public ImageWallpaperRenderer(Context context, SurfaceProxy proxy) {
+ public ImageWallpaperRenderer(Context context) {
final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
if (wpm == null) {
Log.w(TAG, "WallpaperManager not available");
}
mTexture = new WallpaperTexture(wpm);
- DisplayInfo displayInfo = new DisplayInfo();
- context.getDisplay().getDisplayInfo(displayInfo);
-
- // We only do transition in portrait currently, b/137962047.
- int orientation = context.getResources().getConfiguration().orientation;
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
- } else {
- mScissor = new Rect(0, 0, displayInfo.logicalHeight, displayInfo.logicalWidth);
- }
-
- mProxy = proxy;
mProgram = new ImageGLProgram(context);
mWallpaper = new ImageGLWallpaper(mProgram);
- mImageProcessHelper = new ImageProcessHelper();
- mImageRevealHelper = new ImageRevealHelper(this);
-
- startProcessingImage();
- }
-
- protected void startProcessingImage() {
- // Compute threshold of the image, this is an async work.
- mImageProcessHelper.start(mTexture);
}
@Override
@@ -121,103 +84,26 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
@Override
public void onDrawFrame() {
- float threshold = mImageProcessHelper.getThreshold();
- float reveal = mImageRevealHelper.getReveal();
-
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold);
- glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
-
glClear(GL_COLOR_BUFFER_BIT);
- // We only need to scale viewport while doing transition.
- if (mScissorMode) {
- scaleViewport(reveal);
- } else {
- glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
- }
+ glViewport(0, 0, mSurfaceSize.width(), mSurfaceSize.height());
mWallpaper.useTexture();
mWallpaper.draw();
}
@Override
- public void updateAmbientMode(boolean inAmbientMode, long duration) {
- mImageRevealHelper.updateAwake(!inAmbientMode, duration);
- }
-
- @Override
- public void updateOffsets(float xOffset, float yOffset) {
- mXOffset = xOffset;
- mYOffset = yOffset;
- int left = (int) ((mSurfaceSize.width() - mScissor.width()) * xOffset);
- int right = left + mScissor.width();
- mScissor.set(left, mScissor.top, right, mScissor.bottom);
- }
-
- @Override
public Size reportSurfaceSize() {
- mTexture.use(null);
+ mTexture.use(null /* consumer */);
mSurfaceSize.set(mTexture.getTextureDimensions());
return new Size(mSurfaceSize.width(), mSurfaceSize.height());
}
@Override
public void finish() {
- mProxy = null;
- }
-
- private void scaleViewport(float reveal) {
- int left = mScissor.left;
- int top = mScissor.top;
- int width = mScissor.width();
- int height = mScissor.height();
- // Interpolation between SCALE_VIEWPORT_MAX and SCALE_VIEWPORT_MIN by reveal.
- float vpScaled = MathUtils.lerp(SCALE_VIEWPORT_MIN, SCALE_VIEWPORT_MAX, reveal);
- // Calculate the offset amount from the lower left corner.
- float offset = (SCALE_VIEWPORT_MIN - vpScaled) / 2;
- // Change the viewport.
- mViewport.set((int) (left + width * offset), (int) (top + height * offset),
- (int) (width * vpScaled), (int) (height * vpScaled));
- glViewport(mViewport.left, mViewport.top, mViewport.right, mViewport.bottom);
- }
-
- @Override
- public void onRevealStateChanged() {
- mProxy.requestRender();
- }
-
- @Override
- public void onRevealStart(boolean animate) {
- if (animate) {
- mScissorMode = true;
- // Use current display area of texture.
- mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
- }
- mProxy.preRender();
- }
-
- @Override
- public void onRevealEnd() {
- if (mScissorMode) {
- mScissorMode = false;
- // reset texture coordinates to use full texture.
- mWallpaper.adjustTextureCoordinates(null, null, 0, 0);
- // We need draw full texture back before finishing render.
- mProxy.requestRender();
- }
- mProxy.postRender();
}
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
- out.print(prefix); out.print("mProxy="); out.print(mProxy);
out.print(prefix); out.print("mSurfaceSize="); out.print(mSurfaceSize);
- out.print(prefix); out.print("mScissor="); out.print(mScissor);
- out.print(prefix); out.print("mViewport="); out.print(mViewport);
- out.print(prefix); out.print("mScissorMode="); out.print(mScissorMode);
- out.print(prefix); out.print("mXOffset="); out.print(mXOffset);
- out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
- out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
- out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
out.print(prefix); out.print("mWcgContent="); out.print(isWcgContent());
mWallpaper.dump(prefix, fd, out, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index a8a5d896537f..00f693de8f4d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -168,6 +168,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
void synchronizePinnedStackBounds() {
cancelAnimations();
mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
+ mFloatingContentCoordinator.onContentMoved(this);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
new file mode 100644
index 000000000000..fa1b0267fafa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.settings
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.Assert
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Tracks a reference to the context for the current user
+ */
+@Singleton
+class CurrentUserContextTracker @Inject constructor(
+ private val sysuiContext: Context,
+ broadcastDispatcher: BroadcastDispatcher
+) {
+ private val userTracker: CurrentUserTracker
+ var currentUserContext: Context
+
+ init {
+ userTracker = object : CurrentUserTracker(broadcastDispatcher) {
+ override fun onUserSwitched(newUserId: Int) {
+ handleUserSwitched(newUserId)
+ }
+ }
+
+ currentUserContext = makeUserContext(userTracker.currentUserId)
+ }
+
+ fun initialize() {
+ userTracker.startTracking()
+ }
+
+ private fun handleUserSwitched(newUserId: Int) {
+ currentUserContext = makeUserContext(newUserId)
+ }
+
+ private fun makeUserContext(uid: Int): Context {
+ Assert.isMainThread()
+ return sysuiContext.createContextAsUser(
+ UserHandle.getUserHandleForUid(userTracker.currentUserId), 0)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index d37e16b17620..75493f89993b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -699,7 +699,8 @@ public class NotificationEntryManager implements
entry,
oldImportances.get(entry.getKey()),
oldAdjustments.get(entry.getKey()),
- NotificationUiAdjustment.extractFromNotificationEntry(entry));
+ NotificationUiAdjustment.extractFromNotificationEntry(entry),
+ mInflationCallback);
}
updateNotifications("updateNotificationRanking");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index f4c4924b4b9a..710b137d2795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -51,5 +51,6 @@ public interface NotificationRowBinder {
NotificationEntry entry,
@Nullable Integer oldImportance,
NotificationUiAdjustment oldAdjustment,
- NotificationUiAdjustment newAdjustment);
+ NotificationUiAdjustment newAdjustment,
+ NotificationRowContentBinder.InflationCallback callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 73f12f86e52e..e6a4cff0d893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -180,13 +180,14 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
NotificationEntry entry,
@Nullable Integer oldImportance,
NotificationUiAdjustment oldAdjustment,
- NotificationUiAdjustment newAdjustment) {
+ NotificationUiAdjustment newAdjustment,
+ NotificationRowContentBinder.InflationCallback callback) {
if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
if (entry.rowExists()) {
ExpandableNotificationRow row = entry.getRow();
row.reset();
updateRow(entry, row);
- inflateContentViews(entry, row, null /* callback */);
+ inflateContentViews(entry, row, callback);
} else {
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
@@ -221,7 +222,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private void inflateContentViews(
NotificationEntry entry,
ExpandableNotificationRow row,
- NotificationRowContentBinder.InflationCallback inflationCallback) {
+ @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
final boolean useIncreasedCollapsedHeight =
mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance());
final boolean isLowPriority = entry.isAmbient();
@@ -238,7 +239,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
mRowContentBindStage.requestRebind(entry, en -> {
row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setIsLowPriority(isLowPriority);
- inflationCallback.onAsyncInflationFinished(en);
+ if (inflationCallback != null) {
+ inflationCallback.onAsyncInflationFinished(en);
+ }
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 565a082533a7..355990b77544 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -30,6 +30,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.CurrentUserContextTracker;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -53,6 +54,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationPanelLogg
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -60,6 +62,7 @@ import com.android.systemui.util.leak.LeakDetector;
import java.util.concurrent.Executor;
+import javax.inject.Provider;
import javax.inject.Singleton;
import dagger.Binds;
@@ -109,7 +112,9 @@ public interface NotificationsModule {
HighPriorityProvider highPriorityProvider,
INotificationManager notificationManager,
LauncherApps launcherApps,
- ShortcutManager shortcutManager) {
+ ShortcutManager shortcutManager,
+ CurrentUserContextTracker contextTracker,
+ Provider<PriorityOnboardingDialogController.Builder> builderProvider) {
return new NotificationGutsManager(
context,
visualStabilityManager,
@@ -119,7 +124,9 @@ public interface NotificationsModule {
highPriorityProvider,
notificationManager,
launcherApps,
- shortcutManager);
+ shortcutManager,
+ contextTracker,
+ builderProvider);
}
/** Provides an instance of {@link VisualStabilityManager} */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index ab2cffa57c3e..55a593541819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -23,6 +23,7 @@ import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
@@ -45,6 +46,7 @@ import android.graphics.drawable.Icon;
import android.os.Handler;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.transition.ChangeBounds;
@@ -53,7 +55,7 @@ import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Slog;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
@@ -63,6 +65,7 @@ import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -71,6 +74,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.lang.annotation.Retention;
import java.util.List;
+import javax.inject.Provider;
+
/**
* The guts of a conversation notification revealed when performing a long press.
*/
@@ -93,6 +98,9 @@ public class NotificationConversationInfo extends LinearLayout implements
private ShortcutInfo mShortcutInfo;
private String mConversationId;
private StatusBarNotification mSbn;
+ private Notification.BubbleMetadata mBubbleMetadata;
+ private Context mUserContext;
+ private Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
private boolean mIsDeviceProvisioned;
private int mAppBubble;
@@ -136,17 +144,17 @@ public class NotificationConversationInfo extends LinearLayout implements
*/
private OnClickListener mOnFavoriteClick = v -> {
- mSelectedAction = ACTION_FAVORITE;
+ setSelectedAction(ACTION_FAVORITE);
updateToggleActions(mSelectedAction, true);
};
private OnClickListener mOnDefaultClick = v -> {
- mSelectedAction = ACTION_DEFAULT;
+ setSelectedAction(ACTION_DEFAULT);
updateToggleActions(mSelectedAction, true);
};
private OnClickListener mOnMuteClick = v -> {
- mSelectedAction = ACTION_MUTE;
+ setSelectedAction(ACTION_MUTE);
updateToggleActions(mSelectedAction, true);
};
@@ -170,6 +178,23 @@ public class NotificationConversationInfo extends LinearLayout implements
void onClick(View v, int hoursToSnooze);
}
+ @VisibleForTesting
+ void setSelectedAction(int selectedAction) {
+ if (mSelectedAction == selectedAction) {
+ return;
+ }
+
+ mSelectedAction = selectedAction;
+ onSelectedActionChanged();
+ }
+
+ private void onSelectedActionChanged() {
+ // If the user selected Priority, maybe show the priority onboarding
+ if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) {
+ showPriorityOnboarding();
+ }
+ }
+
public void bindNotification(
ShortcutManager shortcutManager,
PackageManager pm,
@@ -181,6 +206,8 @@ public class NotificationConversationInfo extends LinearLayout implements
OnSettingsClickListener onSettingsClick,
OnSnoozeClickListener onSnoozeClickListener,
ConversationIconFactory conversationIconFactory,
+ Context userContext,
+ Provider<PriorityOnboardingDialogController.Builder> builderProvider,
boolean isDeviceProvisioned) {
mSelectedAction = -1;
mINotificationManager = iNotificationManager;
@@ -196,6 +223,9 @@ public class NotificationConversationInfo extends LinearLayout implements
mIsDeviceProvisioned = isDeviceProvisioned;
mOnSnoozeClickListener = onSnoozeClickListener;
mIconFactory = conversationIconFactory;
+ mUserContext = userContext;
+ mBubbleMetadata = entry.getBubbleMetadata();
+ mBuilderProvider = builderProvider;
mShortcutManager = shortcutManager;
mConversationId = mNotificationChannel.getConversationId();
@@ -213,7 +243,7 @@ public class NotificationConversationInfo extends LinearLayout implements
try {
mAppBubble = mINotificationManager.getBubblePreferenceForPackage(mPackageName, mAppUid);
} catch (RemoteException e) {
- Slog.e(TAG, "can't reach OS", e);
+ Log.e(TAG, "can't reach OS", e);
mAppBubble = BUBBLE_PREFERENCE_SELECTED;
}
@@ -491,6 +521,38 @@ public class NotificationConversationInfo extends LinearLayout implements
mAppUid, mSelectedAction, mNotificationChannel));
}
+ private boolean shouldShowPriorityOnboarding() {
+ return !Prefs.getBoolean(mUserContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, false);
+ }
+
+ private void showPriorityOnboarding() {
+ View onboardingView = LayoutInflater.from(mContext)
+ .inflate(R.layout.priority_onboarding_half_shell, null);
+
+ boolean ignoreDnd = false;
+ try {
+ ignoreDnd = (mINotificationManager
+ .getConsolidatedNotificationPolicy().priorityConversationSenders
+ & NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT) != 0;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not check conversation senders", e);
+ }
+
+ boolean showAsBubble = mBubbleMetadata.getAutoExpandBubble()
+ && Settings.Global.getInt(mContext.getContentResolver(),
+ NOTIFICATION_BUBBLES, 0) == 1;
+
+ PriorityOnboardingDialogController controller = mBuilderProvider.get()
+ .setContext(mUserContext)
+ .setView(onboardingView)
+ .setIgnoresDnd(ignoreDnd)
+ .setShowsAsBubble(showAsBubble)
+ .build();
+
+ controller.init();
+ controller.show();
+ }
+
/**
* Closes the controls and commits the updated importance values (indirectly).
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 2487d1a898a3..624fabc0a496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -49,6 +49,7 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.CurrentUserContextTracker;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -67,6 +68,8 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Provider;
+
import dagger.Lazy;
/**
@@ -111,6 +114,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final INotificationManager mNotificationManager;
private final LauncherApps mLauncherApps;
private final ShortcutManager mShortcutManager;
+ private final CurrentUserContextTracker mContextTracker;
+ private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
/**
* Injected constructor. See {@link NotificationsModule}.
@@ -121,7 +126,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
HighPriorityProvider highPriorityProvider,
INotificationManager notificationManager,
LauncherApps launcherApps,
- ShortcutManager shortcutManager) {
+ ShortcutManager shortcutManager,
+ CurrentUserContextTracker contextTracker,
+ Provider<PriorityOnboardingDialogController.Builder> builderProvider) {
mContext = context;
mVisualStabilityManager = visualStabilityManager;
mStatusBarLazy = statusBarLazy;
@@ -131,6 +138,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mNotificationManager = notificationManager;
mLauncherApps = launcherApps;
mShortcutManager = shortcutManager;
+ mContextTracker = contextTracker;
+ mBuilderProvider = builderProvider;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -403,6 +412,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
onSettingsClick,
onSnoozeClickListener,
iconFactoryLoader,
+ mContextTracker.getCurrentUserContext(),
+ mBuilderProvider,
mDeviceProvisionedController.isDeviceProvisioned());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
new file mode 100644
index 000000000000..d1b405256f39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.statusbar.notification.row
+
+import android.app.Dialog
+import android.content.Context
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.drawable.ColorDrawable
+import android.view.Gravity
+import android.view.View
+import android.view.View.GONE
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.Window
+import android.view.WindowInsets.Type.statusBars
+import android.view.WindowManager
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.Prefs
+import com.android.systemui.R
+import java.lang.IllegalStateException
+import javax.inject.Inject
+
+/**
+ * Controller to handle presenting the priority conversations onboarding dialog
+ */
+class PriorityOnboardingDialogController @Inject constructor(
+ val view: View,
+ val context: Context,
+ val ignoresDnd: Boolean,
+ val showsAsBubble: Boolean
+) {
+
+ private lateinit var dialog: Dialog
+
+ fun init() {
+ initDialog()
+ }
+
+ fun show() {
+ dialog.show()
+ }
+
+ private fun done() {
+ // Log that the user has seen the onboarding
+ Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true)
+ dialog.dismiss()
+ }
+
+ class Builder @Inject constructor() {
+ private lateinit var view: View
+ private lateinit var context: Context
+ private var ignoresDnd = false
+ private var showAsBubble = false
+
+ fun setView(v: View): Builder {
+ view = v
+ return this
+ }
+
+ fun setContext(c: Context): Builder {
+ context = c
+ return this
+ }
+
+ fun setIgnoresDnd(ignore: Boolean): Builder {
+ ignoresDnd = ignore
+ return this
+ }
+
+ fun setShowsAsBubble(bubble: Boolean): Builder {
+ showAsBubble = bubble
+ return this
+ }
+
+ fun build(): PriorityOnboardingDialogController {
+ val controller = PriorityOnboardingDialogController(
+ view, context, ignoresDnd, showAsBubble)
+ return controller
+ }
+ }
+
+ private fun initDialog() {
+ dialog = Dialog(context)
+
+ if (dialog.window == null) {
+ throw IllegalStateException("Need a window for the onboarding dialog to show")
+ }
+
+ dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
+ // Prevent a11y readers from reading the first element in the dialog twice
+ dialog.setTitle("\u00A0")
+ dialog.apply {
+ setContentView(view)
+ setCanceledOnTouchOutside(true)
+
+ findViewById<TextView>(R.id.done_button)?.setOnClickListener {
+ done()
+ }
+
+ if (!ignoresDnd) {
+ findViewById<LinearLayout>(R.id.ignore_dnd_tip).visibility = GONE
+ }
+
+ if (!showsAsBubble) {
+ findViewById<LinearLayout>(R.id.floating_bubble_tip).visibility = GONE
+ }
+
+ window?.apply {
+ setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ addFlags(wmFlags)
+ setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ setWindowAnimations(com.android.internal.R.style.Animation_InputMethod)
+
+ attributes = attributes.apply {
+ format = PixelFormat.TRANSLUCENT
+ title = ChannelEditorDialogController::class.java.simpleName
+ gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ fitInsetsTypes = attributes.fitInsetsTypes and statusBars().inv()
+ width = MATCH_PARENT
+ height = WRAP_CONTENT
+ }
+ }
+ }
+ }
+
+ private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
+ or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+}
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 7ac066277c86..e20be2b71939 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
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.row.wrapper;
import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y;
-import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.Notification;
import android.content.Context;
@@ -133,15 +132,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
if (mAppOps != null) {
mAppOps.setOnClickListener(listener);
}
- if (mCameraIcon != null) {
- mCameraIcon.setOnClickListener(listener);
- }
- if (mMicIcon != null) {
- mMicIcon.setOnClickListener(listener);
- }
- if (mOverlayIcon != null) {
- mOverlayIcon.setOnClickListener(listener);
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 6e5f8a0ae5e9..051bd29bc323 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -367,7 +367,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mobileSignalController.unregisterListener();
}
mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
- mContext.unregisterReceiver(this);
+ mBroadcastDispatcher.unregisterReceiver(this);
}
public int getConnectedWifiLevel() {
@@ -859,6 +859,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
pw.println(" - telephony ------");
pw.print(" hasVoiceCallingFeature()=");
pw.println(hasVoiceCallingFeature());
+ pw.println(" mListening=" + mListening);
pw.println(" - connectivity ------");
pw.print(" mConnectedTransports=");
diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
index ca4b67db0d46..242f7cde9d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
@@ -187,16 +187,23 @@ class FloatingContentCoordinator @Inject constructor() {
// Tell that content to get out of the way, and save the bounds it says it's moving
// (or animating) to.
.forEach { (content, bounds) ->
- content.moveToBounds(
- content.calculateNewBoundsOnOverlap(
- conflictingNewBounds,
- // Pass all of the content bounds except the bounds of the
- // content we're asking to move, and the conflicting new bounds
- // (since those are passed separately).
- otherContentBounds = allContentBounds.values
- .minus(bounds)
- .minus(conflictingNewBounds)))
- allContentBounds[content] = content.getFloatingBoundsOnScreen()
+ val newBounds = content.calculateNewBoundsOnOverlap(
+ conflictingNewBounds,
+ // Pass all of the content bounds except the bounds of the
+ // content we're asking to move, and the conflicting new bounds
+ // (since those are passed separately).
+ otherContentBounds = allContentBounds.values
+ .minus(bounds)
+ .minus(conflictingNewBounds))
+
+ // If the new bounds are empty, it means there's no non-overlapping position
+ // that is in bounds. Just leave the content where it is. This should normally
+ // not happen, but sometimes content like PIP reports incorrect bounds
+ // temporarily.
+ if (!newBounds.isEmpty) {
+ content.moveToBounds(newBounds)
+ allContentBounds[content] = content.getFloatingBoundsOnScreen()
+ }
}
currentlyResolvingConflicts = false
@@ -229,8 +236,8 @@ class FloatingContentCoordinator @Inject constructor() {
* @param allowedBounds The area within which we're allowed to find new bounds for the
* content.
* @return New bounds for the content that don't intersect the exclusion rects or the
- * newly overlapping rect, and that is within bounds unless no possible in-bounds position
- * exists.
+ * newly overlapping rect, and that is within bounds - or an empty Rect if no in-bounds
+ * position exists.
*/
@JvmStatic
fun findAreaForContentVertically(
@@ -274,7 +281,13 @@ class FloatingContentCoordinator @Inject constructor() {
!overlappingContentPushingDown && !positionAboveInBounds
// Return the content rect, but offset to reflect the new position.
- return if (usePositionBelow) newContentBoundsBelow else newContentBoundsAbove
+ val newBounds = if (usePositionBelow) newContentBoundsBelow else newContentBoundsAbove
+
+ // If the new bounds are within the allowed bounds, return them. If not, it means that
+ // there are no legal new bounds. This can happen if the new content's bounds are too
+ // large (for example, full-screen PIP). Since there is no reasonable action to take
+ // here, return an empty Rect and we will just not move the content.
+ return if (allowedBounds.contains(newBounds)) newBounds else Rect()
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 5227aaf01249..0c69ffdef372 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -16,7 +16,6 @@
package com.android.systemui;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doReturn;
@@ -32,19 +31,16 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
-import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Size;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceHolder;
import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
-import com.android.systemui.statusbar.phone.DozeParameters;
import org.junit.Before;
import org.junit.Test;
@@ -72,8 +68,6 @@ public class ImageWallpaperTest extends SysuiTestCase {
@Mock
private Bitmap mWallpaperBitmap;
@Mock
- private DozeParameters mDozeParam;
- @Mock
private Handler mHandler;
private CountDownLatch mEventCountdown;
@@ -100,14 +94,13 @@ public class ImageWallpaperTest extends SysuiTestCase {
when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
- when(mDozeParam.getDisplayNeedsBlanking()).thenReturn(false);
}
private ImageWallpaper createImageWallpaper() {
- return new ImageWallpaper(mDozeParam) {
+ return new ImageWallpaper() {
@Override
public Engine onCreateEngine() {
- return new GLEngine(mDozeParam, mHandler) {
+ return new GLEngine(mHandler) {
@Override
public Context getDisplayContext() {
return mMockContext;
@@ -130,75 +123,52 @@ public class ImageWallpaperTest extends SysuiTestCase {
};
}
- private ImageWallpaperRenderer createImageWallpaperRenderer(ImageWallpaper.GLEngine engine) {
- return new ImageWallpaperRenderer(mMockContext, engine) {
- @Override
- public void startProcessingImage() {
- // No - Op
- }
- };
- }
-
@Test
public void testBitmapWallpaper_normal() {
// Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
// Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
- // Finally, we assert the transition will not be stopped.
- verifySurfaceSizeAndAssertTransition(DISPLAY_WIDTH /* bmpWidth */,
+ verifySurfaceSize(DISPLAY_WIDTH /* bmpWidth */,
DISPLAY_WIDTH /* bmpHeight */,
DISPLAY_WIDTH /* surfaceWidth */,
- DISPLAY_WIDTH /* surfaceHeight */,
- false /* assertion */);
+ DISPLAY_WIDTH /* surfaceHeight */);
}
@Test
public void testBitmapWallpaper_low_resolution() {
// Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
// Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
- // Finally, we assert the transition will be stopped.
- verifySurfaceSizeAndAssertTransition(LOW_BMP_WIDTH /* bmpWidth */,
+ verifySurfaceSize(LOW_BMP_WIDTH /* bmpWidth */,
LOW_BMP_HEIGHT /* bmpHeight */,
LOW_BMP_WIDTH /* surfaceWidth */,
- LOW_BMP_HEIGHT /* surfaceHeight */,
- false /* assertion */);
+ LOW_BMP_HEIGHT /* surfaceHeight */);
}
@Test
public void testBitmapWallpaper_too_small() {
// Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT.
// Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT.
- // Finally, we assert the transition will be stopped.
- verifySurfaceSizeAndAssertTransition(INVALID_BMP_WIDTH /* bmpWidth */,
+ verifySurfaceSize(INVALID_BMP_WIDTH /* bmpWidth */,
INVALID_BMP_HEIGHT /* bmpHeight */,
ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
- ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
- false /* assertion */);
+ ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */);
}
- private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
- int surfaceWidth, int surfaceHeight, boolean assertion) {
+ private void verifySurfaceSize(int bmpWidth, int bmpHeight,
+ int surfaceWidth, int surfaceHeight) {
ImageWallpaper.GLEngine wallpaperEngine =
(ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
- when(engineSpy.mIsHighEndGfx).thenReturn(true);
when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth);
when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight);
- ImageWallpaperRenderer renderer = createImageWallpaperRenderer(engineSpy);
+ ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mMockContext);
doReturn(renderer).when(engineSpy).getRendererInstance();
engineSpy.onCreate(engineSpy.getSurfaceHolder());
verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight);
assertWithMessage("setFixedSizeAllowed should have been called.").that(
mEventCountdown.getCount()).isEqualTo(0);
-
- Size frameSize = renderer.reportSurfaceSize();
- Rect frame = new Rect(0, 0, frameSize.getWidth(), frameSize.getHeight());
- when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
-
- assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
- // destroy
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
index 4b47093bb951..e23507b0a895 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -40,6 +40,7 @@ import com.android.systemui.SysuiTestCase;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -84,22 +85,17 @@ public class EglHelperTest extends SysuiTestCase {
}
@Test
- public void testInit_finish() {
+ public void testInit_normal() {
mEglHelper.init(mSurfaceHolder, false /* wideColorGamut */);
assertThat(mEglHelper.hasEglDisplay()).isTrue();
assertThat(mEglHelper.hasEglContext()).isTrue();
assertThat(mEglHelper.hasEglSurface()).isTrue();
verify(mEglHelper).askCreatingEglWindowSurface(
any(SurfaceHolder.class), eq(null), anyInt());
-
- mEglHelper.finish();
- assertThat(mEglHelper.hasEglSurface()).isFalse();
- assertThat(mEglHelper.hasEglContext()).isFalse();
- assertThat(mEglHelper.hasEglDisplay()).isFalse();
}
@Test
- public void testInit_finish_wide_gamut() {
+ public void testInit_wide_gamut() {
// In EglHelper, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
doReturn(0x3490).when(mEglHelper).getWcgCapability();
// In EglHelper, KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace";
@@ -113,10 +109,10 @@ public class EglHelperTest extends SysuiTestCase {
.askCreatingEglWindowSurface(any(SurfaceHolder.class), ac.capture(), anyInt());
assertThat(ac.getValue()).isNotNull();
assertThat(ac.getValue()).isEqualTo(expectedArgument);
- mEglHelper.finish();
}
@Test
+ @Ignore
public void testFinish_shouldNotCrash() {
mEglHelper.terminateEglDisplay();
assertThat(mEglHelper.hasEglDisplay()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
deleted file mode 100644
index c827ac7ab963..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
+++ /dev/null
@@ -1,73 +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.
- */
-
-package com.android.systemui.glwallpaper;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class ImageRevealHelperTest extends SysuiTestCase {
-
- static final int ANIMATION_DURATION = 500;
- ImageRevealHelper mImageRevealHelper;
- ImageRevealHelper.RevealStateListener mRevealStateListener;
-
- @Before
- public void setUp() throws Exception {
- mRevealStateListener = new ImageRevealHelper.RevealStateListener() {
- @Override
- public void onRevealStateChanged() {
- // no-op
- }
-
- @Override
- public void onRevealStart(boolean animate) {
- // no-op
- }
-
- @Override
- public void onRevealEnd() {
- // no-op
- }
- };
- mImageRevealHelper = new ImageRevealHelper(mRevealStateListener);
- }
-
- @Test
- public void testBiometricAuthUnlockAnimateImageRevealState_shouldNotBlackoutScreen() {
- assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
-
- mImageRevealHelper.updateAwake(true /* awake */, ANIMATION_DURATION);
- assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
-
- // When device unlock through Biometric, should not show reveal transition
- mImageRevealHelper.updateAwake(false /* awake */, 0);
- assertThat(mImageRevealHelper.getReveal()).isEqualTo(1f);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
index d61be37692c4..24f3eb27579a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.glwallpaper;
-import static com.android.systemui.glwallpaper.GLWallpaperRenderer.SurfaceProxy;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
@@ -48,30 +46,12 @@ import java.util.Set;
public class ImageWallpaperRendererTest extends SysuiTestCase {
private WallpaperManager mWpmSpy;
- private SurfaceProxy mSurfaceProxy;
@Before
public void setUp() throws Exception {
final WallpaperManager wpm = mContext.getSystemService(WallpaperManager.class);
mWpmSpy = spy(wpm);
mContext.addMockSystemService(WallpaperManager.class, mWpmSpy);
-
- mSurfaceProxy = new SurfaceProxy() {
- @Override
- public void requestRender() {
- // NO-op
- }
-
- @Override
- public void preRender() {
- // No-op
- }
-
- @Override
- public void postRender() {
- // No-op
- }
- };
}
@Test
@@ -91,12 +71,12 @@ public class ImageWallpaperRendererTest extends SysuiTestCase {
doReturn(supportedWideGamuts).when(cmProxySpy).getSupportedColorSpaces();
mWpmSpy.setBitmap(p3Bitmap);
- ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+ ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext);
rendererP3.reportSurfaceSize();
assertThat(rendererP3.isWcgContent()).isTrue();
mWpmSpy.setBitmap(srgbBitmap);
- ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+ ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext);
assertThat(renderer.isWcgContent()).isFalse();
} finally {
srgbBitmap.recycle();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 6bcaee100496..61388b6d0389 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -16,15 +16,12 @@
package com.android.systemui.statusbar.notification.row;
-import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME;
-import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
-import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@@ -38,8 +35,10 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -51,6 +50,7 @@ import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.PendingIntent;
import android.app.Person;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
@@ -61,20 +61,19 @@ import android.content.pm.ShortcutManager;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
-import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
@@ -89,6 +88,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
@@ -99,6 +99,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+import javax.inject.Provider;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -145,6 +147,11 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
private ShadeController mShadeController;
@Mock
private ConversationIconFactory mIconFactory;
+ @Mock
+ private Context mUserContext;
+ @Mock(answer = Answers.RETURNS_SELF)
+ private PriorityOnboardingDialogController.Builder mBuilder;
+ private Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider = () -> mBuilder;
@Before
public void setUp() throws Exception {
@@ -236,6 +243,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
assertEquals(mIconDrawable, view.getDrawable());
@@ -255,6 +264,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
@@ -300,6 +311,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertTrue(textView.getText().toString().contains(group.getName()));
@@ -321,6 +334,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -341,6 +356,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
@@ -368,6 +385,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
@@ -391,6 +410,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
},
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
@@ -412,6 +433,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -434,6 +457,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
},
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
false);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -454,6 +479,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View view = mNotificationInfo.findViewById(R.id.silence);
assertThat(view.isSelected()).isTrue();
@@ -477,6 +504,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
@@ -503,6 +532,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View view = mNotificationInfo.findViewById(R.id.default_behavior);
assertThat(view.isSelected()).isTrue();
@@ -528,6 +559,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View fave = mNotificationInfo.findViewById(R.id.priority);
@@ -566,6 +599,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -603,6 +638,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -641,6 +678,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View fave = mNotificationInfo.findViewById(R.id.priority);
@@ -673,6 +712,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View fave = mNotificationInfo.findViewById(R.id.priority);
@@ -703,6 +744,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -734,6 +777,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -765,6 +810,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
@@ -795,6 +842,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -824,6 +873,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
@@ -844,9 +895,81 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
null,
mIconFactory,
+ mUserContext,
+ mBuilderProvider,
true);
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
}
+
+ @Test
+ public void testSelectPriorityPresentsOnboarding_firstTime() {
+ // GIVEN pref is false
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, false);
+
+ // GIVEN the priority onboarding screen is present
+ PriorityOnboardingDialogController.Builder b =
+ new PriorityOnboardingDialogController.Builder();
+ PriorityOnboardingDialogController controller =
+ mock(PriorityOnboardingDialogController.class);
+ when(b.build()).thenReturn(controller);
+
+ // GIVEN the user is changing conversation settings
+ when(mBuilderProvider.get()).thenReturn(b);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ mIconFactory,
+ mUserContext,
+ mBuilderProvider,
+ true);
+
+ // WHEN user clicks "priority"
+ mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
+
+ // THEN the user is presented with the priority onboarding screen
+ verify(controller, atLeastOnce()).show();
+ }
+
+ @Test
+ public void testSelectPriorityDoesNotShowOnboarding_secondTime() {
+ //WHEN pref is true
+ Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true);
+
+ PriorityOnboardingDialogController.Builder b =
+ new PriorityOnboardingDialogController.Builder();
+ PriorityOnboardingDialogController controller =
+ mock(PriorityOnboardingDialogController.class);
+ when(b.build()).thenReturn(controller);
+
+ when(mBuilderProvider.get()).thenReturn(b);
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ mIconFactory,
+ mUserContext,
+ mBuilderProvider,
+ true);
+
+ // WHEN user clicks "priority"
+ mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE);
+
+ // THEN the user is presented with the priority onboarding screen
+ verify(controller, never()).show();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index ed4642344dba..5813740712b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -66,6 +66,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.settings.CurrentUserContextTracker;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -83,11 +84,14 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import javax.inject.Provider;
+
/**
* Tests for {@link NotificationGutsManager}.
*/
@@ -120,6 +124,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private LauncherApps mLauncherApps;
@Mock private ShortcutManager mShortcutManager;
@Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ @Mock private CurrentUserContextTracker mContextTracker;
+ @Mock(answer = Answers.RETURNS_SELF)
+ private PriorityOnboardingDialogController.Builder mBuilder;
+ private Provider<PriorityOnboardingDialogController.Builder> mProvider = () -> mBuilder;
@Before
public void setUp() {
@@ -136,7 +144,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager,
() -> mStatusBar, mHandler, mAccessibilityManager, mHighPriorityProvider,
- mINotificationManager, mLauncherApps, mShortcutManager);
+ mINotificationManager, mLauncherApps, mShortcutManager, mContextTracker, mProvider);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index 1dc8227e81f4..2b2fe4534c3e 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -34,11 +34,14 @@
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
+
<application
android:process="com.android.networkstack.process"
android:extractNativeLibs="false"
diff --git a/packages/Tethering/res/values-mcc204-mnc04/strings.xml b/packages/Tethering/res/values-mcc204-mnc04/strings.xml
deleted file mode 100644
index 9dadd49cf8a4..000000000000
--- a/packages/Tethering/res/values-mcc204-mnc04/strings.xml
+++ /dev/null
@@ -1,30 +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.
--->
-<resources>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_title">Tethering has no internet</string>
- <!-- String for no upstream notification title [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_message">Devices can\u2019t connect</string>
- <!-- String for no upstream notification disable button [CHAR LIMIT=200] -->
- <string name="no_upstream_notification_disable_button">Turn off tethering</string>
-
- <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
- <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string>
- <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
- <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string>
- <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
- <string name="upstream_roaming_notification_continue_button">Continue</string>
-</resources>
diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml
new file mode 100644
index 000000000000..8c627d5df058
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004/config.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<resources>
+ <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+ "0" for disable this feature. -->
+ <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml
new file mode 100644
index 000000000000..8c627d5df058
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480/config.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<resources>
+ <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+ "0" for disable this feature. -->
+ <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 430fdc42284d..52aa5bbaffa5 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -202,4 +202,10 @@
<string name="tethering_notification_title">@string/tethered_notification_title</string>
<!-- String for tether enable notification message. -->
<string name="tethering_notification_message">@string/tethered_notification_message</string>
+
+ <!-- No upstream notification is shown when there is a downstream but no upstream that is able
+ to do the tethering. -->
+ <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
+ "-1" for disable this feature. -->
+ <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>
</resources>
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 4c7b2d49ee9a..049a9f68bbd2 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -38,8 +38,6 @@ import android.content.IntentFilter;
import android.net.util.SharedLog;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
@@ -75,11 +73,6 @@ public class EntitlementManager {
private final ComponentName mSilentProvisioningService;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
- private static final int EVENT_START_PROVISIONING = 0;
- private static final int EVENT_STOP_PROVISIONING = 1;
- private static final int EVENT_UPSTREAM_CHANGED = 2;
- private static final int EVENT_MAYBE_RUN_PROVISIONING = 3;
- private static final int EVENT_GET_ENTITLEMENT_VALUE = 4;
// The ArraySet contains enabled downstream types, ex:
// {@link TetheringManager.TETHERING_WIFI}
@@ -90,7 +83,7 @@ public class EntitlementManager {
private final int mPermissionChangeMessageCode;
private final SharedLog mLog;
private final SparseIntArray mEntitlementCacheValue;
- private final EntitlementHandler mHandler;
+ private final Handler mHandler;
private final StateMachine mTetherMasterSM;
// Key: TetheringManager.TETHERING_*(downstream).
// Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
@@ -112,10 +105,7 @@ public class EntitlementManager {
mEntitlementCacheValue = new SparseIntArray();
mTetherMasterSM = tetherMasterSM;
mPermissionChangeMessageCode = permissionChangeMessageCode;
- final Handler masterHandler = tetherMasterSM.getHandler();
- // Create entitlement's own handler which is associated with TetherMaster thread
- // let all entitlement processes run in the same thread.
- mHandler = new EntitlementHandler(masterHandler.getLooper());
+ mHandler = tetherMasterSM.getHandler();
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
null, mHandler);
mSilentProvisioningService = ComponentName.unflattenFromString(
@@ -172,14 +162,9 @@ public class EntitlementManager {
* provisioning app UI if there is one.
*/
public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_START_PROVISIONING,
- downstreamType, encodeBool(showProvisioningUi)));
- }
-
- private void handleStartProvisioningIfNeeded(int type, boolean showProvisioningUi) {
- if (!isValidDownstreamType(type)) return;
+ if (!isValidDownstreamType(downstreamType)) return;
- if (!mCurrentTethers.contains(type)) mCurrentTethers.add(type);
+ if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType);
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
if (isTetherProvisioningRequired(config)) {
@@ -192,9 +177,9 @@ public class EntitlementManager {
// till upstream change to cellular.
if (mUsingCellularAsUpstream) {
if (showProvisioningUi) {
- runUiTetherProvisioning(type, config.activeDataSubId);
+ runUiTetherProvisioning(downstreamType, config.activeDataSubId);
} else {
- runSilentTetherProvisioning(type, config.activeDataSubId);
+ runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
}
mNeedReRunProvisioningUi = false;
} else {
@@ -211,10 +196,6 @@ public class EntitlementManager {
* @param type tethering type from TetheringManager.TETHERING_{@code *}
*/
public void stopProvisioningIfNeeded(int type) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_STOP_PROVISIONING, type, 0));
- }
-
- private void handleStopProvisioningIfNeeded(int type) {
if (!isValidDownstreamType(type)) return;
mCurrentTethers.remove(type);
@@ -230,11 +211,6 @@ public class EntitlementManager {
* @param isCellular whether tethering upstream is cellular.
*/
public void notifyUpstream(boolean isCellular) {
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_UPSTREAM_CHANGED, encodeBool(isCellular), 0));
- }
-
- private void handleNotifyUpstream(boolean isCellular) {
if (DBG) {
mLog.i("notifyUpstream: " + isCellular
+ ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
@@ -244,16 +220,17 @@ public class EntitlementManager {
if (mUsingCellularAsUpstream) {
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- handleMaybeRunProvisioning(config);
+ maybeRunProvisioning(config);
}
}
/** Run provisioning if needed */
public void maybeRunProvisioning() {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_MAYBE_RUN_PROVISIONING));
+ final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
+ maybeRunProvisioning(config);
}
- private void handleMaybeRunProvisioning(final TetheringConfiguration config) {
+ private void maybeRunProvisioning(final TetheringConfiguration config) {
if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) {
return;
}
@@ -319,7 +296,7 @@ public class EntitlementManager {
}
if (mUsingCellularAsUpstream) {
- handleMaybeRunProvisioning(config);
+ maybeRunProvisioning(config);
}
}
@@ -494,46 +471,6 @@ public class EntitlementManager {
}
};
- private class EntitlementHandler extends Handler {
- EntitlementHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_START_PROVISIONING:
- handleStartProvisioningIfNeeded(msg.arg1, toBool(msg.arg2));
- break;
- case EVENT_STOP_PROVISIONING:
- handleStopProvisioningIfNeeded(msg.arg1);
- break;
- case EVENT_UPSTREAM_CHANGED:
- handleNotifyUpstream(toBool(msg.arg1));
- break;
- case EVENT_MAYBE_RUN_PROVISIONING:
- final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- handleMaybeRunProvisioning(config);
- break;
- case EVENT_GET_ENTITLEMENT_VALUE:
- handleRequestLatestTetheringEntitlementValue(msg.arg1,
- (ResultReceiver) msg.obj, toBool(msg.arg2));
- break;
- default:
- mLog.log("Unknown event: " + msg.what);
- break;
- }
- }
- }
-
- private static boolean toBool(int encodedBoolean) {
- return encodedBoolean != 0;
- }
-
- private static int encodeBool(boolean b) {
- return b ? 1 : 0;
- }
-
private static boolean isValidDownstreamType(int type) {
switch (type) {
case TETHERING_BLUETOOTH:
@@ -644,13 +581,6 @@ public class EntitlementManager {
/** Get the last value of the tethering entitlement check. */
public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
boolean showEntitlementUi) {
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
- downstream, encodeBool(showEntitlementUi), receiver));
-
- }
-
- private void handleRequestLatestTetheringEntitlementValue(int downstream,
- ResultReceiver receiver, boolean showEntitlementUi) {
if (!isValidDownstreamType(downstream)) {
receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
return;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index bae54a5c76b9..da8bf54718e9 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -257,7 +257,7 @@ public class Tethering {
mContext = mDeps.getContext();
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
- mNotificationUpdater = mDeps.getNotificationUpdater(mContext);
+ mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
mPublicSync = new Object();
@@ -337,6 +337,11 @@ public class Tethering {
filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
mContext.registerReceiver(mStateReceiver, filter, null, mHandler);
+ final IntentFilter noUpstreamFilter = new IntentFilter();
+ noUpstreamFilter.addAction(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING);
+ mContext.registerReceiver(
+ mStateReceiver, noUpstreamFilter, PERMISSION_MAINLINE_NETWORK_STACK, mHandler);
+
final WifiManager wifiManager = getWifiManager();
if (wifiManager != null) {
wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
@@ -855,6 +860,8 @@ public class Tethering {
} else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
mLog.log("OBSERVED data saver changed");
handleDataSaverChanged();
+ } else if (action.equals(TetheringNotificationUpdater.ACTION_DISABLE_TETHERING)) {
+ untetherAll();
}
}
@@ -1946,10 +1953,12 @@ public class Tethering {
/** Get the latest value of the tethering entitlement check. */
void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
boolean showEntitlementUi) {
- if (receiver != null) {
+ if (receiver == null) return;
+
+ mHandler.post(() -> {
mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver,
showEntitlementUi);
- }
+ });
}
/** Register tethering event callback */
@@ -2013,6 +2022,7 @@ public class Tethering {
} finally {
mTetheringEventCallbacks.finishBroadcast();
}
+ mNotificationUpdater.onUpstreamNetworkChanged(network);
}
private void reportConfigurationChanged(TetheringConfigurationParcel config) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 893c5823dce1..9b54b5ff2403 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -106,8 +106,9 @@ public abstract class TetheringDependencies {
/**
* Get a reference to the TetheringNotificationUpdater to be used by tethering.
*/
- public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) {
- return new TetheringNotificationUpdater(ctx);
+ public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx,
+ @NonNull final Looper looper) {
+ return new TetheringNotificationUpdater(ctx, looper);
}
/**
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index 42870560cb5e..ff83fd1e4f1e 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -19,18 +19,25 @@ package com.android.networkstack.tethering;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.text.TextUtils.isEmpty;
import android.app.Notification;
+import android.app.Notification.Action;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.net.Network;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
+import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.SparseArray;
@@ -39,9 +46,13 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.Arrays;
+import java.util.List;
+
/**
* A class to display tethering-related notifications.
*
@@ -58,12 +69,22 @@ public class TetheringNotificationUpdater {
private static final String WIFI_DOWNSTREAM = "WIFI";
private static final String USB_DOWNSTREAM = "USB";
private static final String BLUETOOTH_DOWNSTREAM = "BT";
+ @VisibleForTesting
+ static final String ACTION_DISABLE_TETHERING =
+ "com.android.server.connectivity.tethering.DISABLE_TETHERING";
private static final boolean NOTIFY_DONE = true;
private static final boolean NO_NOTIFY = false;
- // Id to update and cancel tethering notification. Must be unique within the tethering app.
- private static final int ENABLE_NOTIFICATION_ID = 1000;
+ @VisibleForTesting
+ static final int EVENT_SHOW_NO_UPSTREAM = 1;
+ // Id to update and cancel enable notification. Must be unique within the tethering app.
+ @VisibleForTesting
+ static final int ENABLE_NOTIFICATION_ID = 1000;
// Id to update and cancel restricted notification. Must be unique within the tethering app.
- private static final int RESTRICTED_NOTIFICATION_ID = 1001;
+ @VisibleForTesting
+ static final int RESTRICTED_NOTIFICATION_ID = 1001;
+ // Id to update and cancel no upstream notification. Must be unique within the tethering app.
+ @VisibleForTesting
+ static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
@VisibleForTesting
static final int NO_ICON_ID = 0;
@VisibleForTesting
@@ -71,14 +92,16 @@ public class TetheringNotificationUpdater {
private final Context mContext;
private final NotificationManager mNotificationManager;
private final NotificationChannel mChannel;
+ private final Handler mHandler;
// WARNING : the constructor is called on a different thread. Thread safety therefore
- // relies on this value being initialized to 0, and not any other value. If you need
+ // relies on these values being initialized to 0 or false, and not any other value. If you need
// to change this, you will need to change the thread where the constructor is invoked,
// or to introduce synchronization.
// Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
// This value has to be made 1 2 and 4, and OR'd with the others.
private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+ private boolean mNoUpstream = false;
// WARNING : this value is not able to being initialized to 0 and must have volatile because
// telephony service is not guaranteed that is up before tethering service starts. If telephony
@@ -87,10 +110,30 @@ public class TetheringNotificationUpdater {
// INVALID_SUBSCRIPTION_ID.
private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID})
+ @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID})
@interface NotificationId {}
- public TetheringNotificationUpdater(@NonNull final Context context) {
+ private static final class MccMncOverrideInfo {
+ public final List<String> visitedMccMncs;
+ public final int homeMcc;
+ public final int homeMnc;
+ MccMncOverrideInfo(List<String> visitedMccMncs, int mcc, int mnc) {
+ this.visitedMccMncs = visitedMccMncs;
+ this.homeMcc = mcc;
+ this.homeMnc = mnc;
+ }
+ }
+
+ private static final SparseArray<MccMncOverrideInfo> sCarrierIdToMccMnc = new SparseArray<>();
+
+ static {
+ // VZW
+ sCarrierIdToMccMnc.put(
+ 1839, new MccMncOverrideInfo(Arrays.asList(new String[] {"20404"}), 311, 480));
+ }
+
+ public TetheringNotificationUpdater(@NonNull final Context context,
+ @NonNull final Looper looper) {
mContext = context;
mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -99,6 +142,22 @@ public class TetheringNotificationUpdater {
context.getResources().getString(R.string.notification_channel_tethering_status),
NotificationManager.IMPORTANCE_LOW);
mNotificationManager.createNotificationChannel(mChannel);
+ mHandler = new NotificationHandler(looper);
+ }
+
+ private class NotificationHandler extends Handler {
+ NotificationHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case EVENT_SHOW_NO_UPSTREAM:
+ notifyTetheringNoUpstream();
+ break;
+ }
+ }
}
/** Called when downstream has changed */
@@ -106,6 +165,7 @@ public class TetheringNotificationUpdater {
if (mDownstreamTypesMask == downstreamTypesMask) return;
mDownstreamTypesMask = downstreamTypesMask;
updateEnableNotification();
+ updateNoUpstreamNotification();
}
/** Called when active data subscription id changed */
@@ -113,21 +173,62 @@ public class TetheringNotificationUpdater {
if (mActiveDataSubId == subId) return;
mActiveDataSubId = subId;
updateEnableNotification();
+ updateNoUpstreamNotification();
}
+ /** Called when upstream network changed */
+ public void onUpstreamNetworkChanged(@Nullable final Network network) {
+ final boolean isNoUpstream = (network == null);
+ if (mNoUpstream == isNoUpstream) return;
+ mNoUpstream = isNoUpstream;
+ updateNoUpstreamNotification();
+ }
+
+ @NonNull
@VisibleForTesting
- Resources getResourcesForSubId(@NonNull final Context c, final int subId) {
- return SubscriptionManager.getResourcesForSubId(c, subId);
+ final Handler getHandler() {
+ return mHandler;
+ }
+
+ @NonNull
+ @VisibleForTesting
+ Resources getResourcesForSubId(@NonNull final Context context, final int subId) {
+ final Resources res = SubscriptionManager.getResourcesForSubId(context, subId);
+ final TelephonyManager tm =
+ ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+ .createForSubscriptionId(mActiveDataSubId);
+ final int carrierId = tm.getSimCarrierId();
+ final String mccmnc = tm.getSimOperator();
+ final MccMncOverrideInfo overrideInfo = sCarrierIdToMccMnc.get(carrierId);
+ if (overrideInfo != null && overrideInfo.visitedMccMncs.contains(mccmnc)) {
+ // Re-configure MCC/MNC value to specific carrier to get right resources.
+ final Configuration config = res.getConfiguration();
+ config.mcc = overrideInfo.homeMcc;
+ config.mnc = overrideInfo.homeMnc;
+ return context.createConfigurationContext(config).getResources();
+ }
+ return res;
}
private void updateEnableNotification() {
- final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
+ final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
if (tetheringInactive || setupNotification() == NO_NOTIFY) {
clearNotification(ENABLE_NOTIFICATION_ID);
}
}
+ private void updateNoUpstreamNotification() {
+ final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
+
+ if (tetheringInactive
+ || !mNoUpstream
+ || setupNoUpstreamNotification() == NO_NOTIFY) {
+ clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
+ mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
+ }
+ }
+
@VisibleForTesting
void tetheringRestrictionLifted() {
clearNotification(RESTRICTED_NOTIFICATION_ID);
@@ -142,9 +243,38 @@ public class TetheringNotificationUpdater {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final String title = res.getString(R.string.disable_tether_notification_title);
final String message = res.getString(R.string.disable_tether_notification_message);
+ if (isEmpty(title) || isEmpty(message)) return;
+
+ final PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ new Intent(Settings.ACTION_TETHER_SETTINGS),
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null /* options */);
+
+ showNotification(R.drawable.stat_sys_tether_general, title, message,
+ RESTRICTED_NOTIFICATION_ID, pi, new Action[0]);
+ }
+
+ private void notifyTetheringNoUpstream() {
+ final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+ final String title = res.getString(R.string.no_upstream_notification_title);
+ final String message = res.getString(R.string.no_upstream_notification_message);
+ final String disableButton =
+ res.getString(R.string.no_upstream_notification_disable_button);
+ if (isEmpty(title) || isEmpty(message) || isEmpty(disableButton)) return;
+
+ final Intent intent = new Intent(ACTION_DISABLE_TETHERING);
+ intent.setPackage(mContext.getPackageName());
+ final PendingIntent pi = PendingIntent.getBroadcast(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ intent,
+ 0 /* flags */);
+ final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build();
showNotification(R.drawable.stat_sys_tether_general, title, message,
- RESTRICTED_NOTIFICATION_ID);
+ NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action);
}
/**
@@ -179,12 +309,13 @@ public class TetheringNotificationUpdater {
*
* @return {@link android.util.SparseArray} with downstream types and icon id info.
*/
+ @NonNull
@VisibleForTesting
SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) {
final String[] array = res.getStringArray(id);
final SparseArray<Integer> icons = new SparseArray<>();
for (String config : array) {
- if (TextUtils.isEmpty(config)) continue;
+ if (isEmpty(config)) continue;
final String[] elements = config.split(";");
if (elements.length != 2) {
@@ -204,6 +335,18 @@ public class TetheringNotificationUpdater {
return icons;
}
+ private boolean setupNoUpstreamNotification() {
+ final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+ final int delayToShowUpstreamNotification =
+ res.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul);
+
+ if (delayToShowUpstreamNotification < 0) return NO_NOTIFY;
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_NO_UPSTREAM),
+ delayToShowUpstreamNotification);
+ return NOTIFY_DONE;
+ }
+
private boolean setupNotification() {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final SparseArray<Integer> downstreamIcons =
@@ -214,17 +357,22 @@ public class TetheringNotificationUpdater {
final String title = res.getString(R.string.tethering_notification_title);
final String message = res.getString(R.string.tethering_notification_message);
+ if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
- showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID);
+ final PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ new Intent(Settings.ACTION_TETHER_SETTINGS),
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null /* options */);
+
+ showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID, pi, new Action[0]);
return NOTIFY_DONE;
}
private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
- @NonNull final String message, @NotificationId final int id) {
- final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
- final PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0),
- 0 /* requestCode */, intent, 0 /* flags */, null /* options */);
+ @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi,
+ @NonNull final Action... actions) {
final Notification notification =
new Notification.Builder(mContext, mChannel.getId())
.setSmallIcon(iconId)
@@ -236,6 +384,7 @@ public class TetheringNotificationUpdater {
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setCategory(Notification.CATEGORY_STATUS)
.setContentIntent(pi)
+ .setActions(actions)
.build();
mNotificationManager.notify(null /* tag */, id, notification);
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
index 55640db69324..31eaabff5274 100644
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.tethering.tests.unit">
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
<application android:debuggable="true">
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 7bff74b25d94..5f8858857c75 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -23,14 +23,26 @@ import android.content.res.Resources
import android.net.ConnectivityManager.TETHERING_BLUETOOTH
import android.net.ConnectivityManager.TETHERING_USB
import android.net.ConnectivityManager.TETHERING_WIFI
+import android.net.Network
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
import android.os.UserHandle
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
-import androidx.test.platform.app.InstrumentationRegistry
+import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.android.internal.util.test.BroadcastInterceptingContext
import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
+import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
+import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
+import com.android.testutils.waitForIdle
+import org.junit.After
import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,8 +55,8 @@ import org.mockito.Mockito.doReturn
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
-import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
const val TEST_SUBID = 1
@@ -55,10 +67,13 @@ const val GENERAL_ICON_ID = 4
const val WIFI_MASK = 1 shl TETHERING_WIFI
const val USB_MASK = 1 shl TETHERING_USB
const val BT_MASK = 1 shl TETHERING_BLUETOOTH
-const val TITTLE = "Tethering active"
+const val TITLE = "Tethering active"
const val MESSAGE = "Tap here to set up."
-const val TEST_TITTLE = "Hotspot active"
+const val TEST_TITLE = "Hotspot active"
const val TEST_MESSAGE = "Tap to set up hotspot."
+const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access"
+const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet."
+const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot"
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -67,12 +82,15 @@ class TetheringNotificationUpdaterTest {
// should crash if they are used before being initialized.
@Mock private lateinit var mockContext: Context
@Mock private lateinit var notificationManager: NotificationManager
+ @Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var defaultResources: Resources
@Mock private lateinit var testResources: Resources
- // lateinit for this class under test, as it should be reset to a different instance for every
- // tests but should always be initialized before use (or the test should crash).
+ // lateinit for these classes under test, as they should be reset to a different instance for
+ // every test but should always be initialized before use (or the test should crash).
+ private lateinit var context: TestContext
private lateinit var notificationUpdater: TetheringNotificationUpdater
+ private lateinit var fakeTetheringThread: HandlerThread
private val ENABLE_ICON_CONFIGS = arrayOf(
"USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth",
@@ -82,11 +100,19 @@ class TetheringNotificationUpdaterTest {
private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
override fun createContextAsUser(user: UserHandle, flags: Int) =
if (user == UserHandle.ALL) mockContext else this
+ override fun getSystemService(name: String) =
+ if (name == Context.TELEPHONY_SERVICE) telephonyManager
+ else super.getSystemService(name)
}
- private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) {
+ private inner class WrappedNotificationUpdater(c: Context, looper: Looper)
+ : TetheringNotificationUpdater(c, looper) {
override fun getResourcesForSubId(context: Context, subId: Int) =
- if (subId == TEST_SUBID) testResources else defaultResources
+ when (subId) {
+ TEST_SUBID -> testResources
+ INVALID_SUBSCRIPTION_ID -> defaultResources
+ else -> super.getResourcesForSubId(context, subId)
+ }
}
private fun setupResources() {
@@ -94,12 +120,20 @@ class TetheringNotificationUpdaterTest {
.getStringArray(R.array.tethering_notification_icons)
doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources)
.getStringArray(R.array.tethering_notification_icons)
- doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
+ doReturn(5).`when`(testResources)
+ .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+ doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
doReturn(MESSAGE).`when`(defaultResources)
.getString(R.string.tethering_notification_message)
- doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title)
+ doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title)
doReturn(TEST_MESSAGE).`when`(testResources)
.getString(R.string.tethering_notification_message)
+ doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources)
+ .getString(R.string.no_upstream_notification_title)
+ doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources)
+ .getString(R.string.no_upstream_notification_message)
+ doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources)
+ .getString(R.string.no_upstream_notification_disable_button)
doReturn(USB_ICON_ID).`when`(defaultResources)
.getIdentifier(eq("android.test:drawable/usb"), any(), any())
doReturn(BT_ICON_ID).`when`(defaultResources)
@@ -113,35 +147,61 @@ class TetheringNotificationUpdaterTest {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- val context = TestContext(InstrumentationRegistry.getInstrumentation().context)
+ context = TestContext(InstrumentationRegistry.getInstrumentation().context)
doReturn(notificationManager).`when`(mockContext)
.getSystemService(Context.NOTIFICATION_SERVICE)
- notificationUpdater = WrappedNotificationUpdater(context)
+ fakeTetheringThread = HandlerThread(this::class.simpleName)
+ fakeTetheringThread.start()
+ notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper)
setupResources()
}
+ @After
+ fun tearDown() {
+ fakeTetheringThread.quitSafely()
+ }
+
private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
- private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") {
- verify(notificationManager, never()).cancel(any(), anyInt())
+ private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) {
+ verify(notificationManager, never()).cancel(any(), eq(id))
val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
verify(notificationManager, times(1))
- .notify(any(), anyInt(), notificationCaptor.capture())
+ .notify(any(), eq(id), notificationCaptor.capture())
val notification = notificationCaptor.getValue()
assertEquals(iconId, notification.smallIcon.resId)
assertEquals(title, notification.title())
assertEquals(text, notification.text())
+ }
+
+ private fun verifyNotificationCancelled(id: Int) =
+ verify(notificationManager, times(1)).cancel(any(), eq(id))
+ private val tetheringActiveNotifications =
+ listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID)
+
+ private fun verifyCancelAllTetheringActiveNotifications() {
+ tetheringActiveNotifications.forEach {
+ verifyNotificationCancelled(it)
+ }
reset(notificationManager)
}
- private fun verifyNoNotification() {
- verify(notificationManager, times(1)).cancel(any(), anyInt())
- verify(notificationManager, never()).notify(any(), anyInt(), any())
-
+ private fun verifyOnlyTetheringActiveNotification(
+ notifyId: Int,
+ iconId: Int,
+ title: String,
+ text: String
+ ) {
+ tetheringActiveNotifications.forEach {
+ when (it) {
+ notifyId -> verifyNotification(iconId, title, text, notifyId)
+ else -> verifyNotificationCancelled(it)
+ }
+ }
reset(notificationManager)
}
@@ -149,7 +209,7 @@ class TetheringNotificationUpdaterTest {
fun testNotificationWithDownstreamChanged() {
// Wifi downstream. No notification.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
// Same downstream changed. Nothing happened.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
@@ -157,22 +217,23 @@ class TetheringNotificationUpdaterTest {
// Wifi and usb downstreams. Show enable notification
notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK)
- verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE)
// Usb downstream. Still show enable notification.
notificationUpdater.onDownstreamChanged(USB_MASK)
- verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+ verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
}
@Test
fun testNotificationWithActiveDataSubscriptionIdChanged() {
// Usb downstream. Showed enable notification with default resource.
notificationUpdater.onDownstreamChanged(USB_MASK)
- verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+ verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
// Same subId changed. Nothing happened.
notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
@@ -180,15 +241,16 @@ class TetheringNotificationUpdaterTest {
// Set test sub id. Clear notification with test resource.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
// Wifi downstream. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
}
private fun assertIconNumbers(number: Int, configs: Array<String?>) {
@@ -227,10 +289,8 @@ class TetheringNotificationUpdaterTest {
@Test
fun testSetupRestrictedNotification() {
- val title = InstrumentationRegistry.getInstrumentation().context.resources
- .getString(R.string.disable_tether_notification_title)
- val message = InstrumentationRegistry.getInstrumentation().context.resources
- .getString(R.string.disable_tether_notification_message)
+ val title = context.resources.getString(R.string.disable_tether_notification_title)
+ val message = context.resources.getString(R.string.disable_tether_notification_message)
val disallowTitle = "Tether function is disallowed"
val disallowMessage = "Please contact your admin"
doReturn(title).`when`(defaultResources)
@@ -244,18 +304,127 @@ class TetheringNotificationUpdaterTest {
// User restrictions on. Show restricted notification.
notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(R.drawable.stat_sys_tether_general, title, message)
+ verifyNotification(R.drawable.stat_sys_tether_general, title, message,
+ RESTRICTED_NOTIFICATION_ID)
+ reset(notificationManager)
// User restrictions off. Clear notification.
notificationUpdater.tetheringRestrictionLifted()
- verifyNoNotification()
+ verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID)
+ reset(notificationManager)
// Set test sub id. No notification.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyNoNotification()
+ verifyCancelAllTetheringActiveNotifications()
// User restrictions on again. Show restricted notification with test resource.
notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage)
+ verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage,
+ RESTRICTED_NOTIFICATION_ID)
+ reset(notificationManager)
+ }
+
+ val MAX_BACKOFF_MS = 200L
+ /**
+ * Waits for all messages, including delayed ones, to be processed.
+ *
+ * This will wait until the handler has no more messages to be processed including
+ * delayed ones, or the timeout has expired. It uses an exponential backoff strategy
+ * to wait longer and longer to consume less CPU, with the max granularity being
+ * MAX_BACKOFF_MS.
+ *
+ * @return true if all messages have been processed including delayed ones, false if timeout
+ *
+ * TODO: Move this method to com.android.testutils.HandlerUtils.kt.
+ */
+ private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) {
+ fun hasMatchingMessages() =
+ if (what == null) hasMessagesOrCallbacks() else hasMessages(what)
+ val expiry = System.currentTimeMillis() + timeoutMs
+ var delay = 5L
+ while (System.currentTimeMillis() < expiry && hasMatchingMessages()) {
+ // None of Handler, Looper, Message and MessageQueue expose any way to retrieve
+ // the time when the next (let alone the last) message will be processed, so
+ // short of examining the internals with reflection sleep() is the only solution.
+ Thread.sleep(delay)
+ delay = (delay * 2)
+ .coerceAtMost(expiry - System.currentTimeMillis())
+ .coerceAtMost(MAX_BACKOFF_MS)
+ }
+
+ val timeout = expiry - System.currentTimeMillis()
+ if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms")
+ waitForIdle(timeout)
+ }
+
+ @Test
+ fun testNotificationWithUpstreamNetworkChanged() {
+ // Set test sub id. No notification.
+ notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+ verifyCancelAllTetheringActiveNotifications()
+
+ // Wifi downstream. Show enable notification with test resource.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+
+ // There is no upstream. Show no upstream notification.
+ notificationUpdater.onUpstreamNetworkChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+ verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
+ TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
+ reset(notificationManager)
+
+ // Same upstream network changed. Nothing happened.
+ notificationUpdater.onUpstreamNetworkChanged(null)
+ verifyZeroInteractions(notificationManager)
+
+ // Upstream come back. Clear no upstream notification.
+ notificationUpdater.onUpstreamNetworkChanged(Network(1000))
+ verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID)
+ reset(notificationManager)
+
+ // No upstream again. Show no upstream notification.
+ notificationUpdater.onUpstreamNetworkChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+ verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
+ TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
+ reset(notificationManager)
+
+ // No downstream. No notification.
+ notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+ verifyCancelAllTetheringActiveNotifications()
+
+ // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream
+ // again. Show enable notification only.
+ doReturn(-1).`when`(testResources)
+ .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
+ verifyOnlyTetheringActiveNotification(
+ ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+ }
+
+ @Test
+ fun testGetResourcesForSubId() {
+ doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt())
+ doReturn(1234).`when`(telephonyManager).getSimCarrierId()
+ doReturn("000000").`when`(telephonyManager).getSimOperator()
+
+ val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId.
+ val config = context.resources.configuration
+ var res = notificationUpdater.getResourcesForSubId(context, subId)
+ assertEquals(config.mcc, res.configuration.mcc)
+ assertEquals(config.mnc, res.configuration.mnc)
+
+ doReturn(1839).`when`(telephonyManager).getSimCarrierId()
+ res = notificationUpdater.getResourcesForSubId(context, subId)
+ assertEquals(config.mcc, res.configuration.mcc)
+ assertEquals(config.mnc, res.configuration.mnc)
+
+ doReturn("20404").`when`(telephonyManager).getSimOperator()
+ res = notificationUpdater.getResourcesForSubId(context, subId)
+ assertEquals(311, res.configuration.mcc)
+ assertEquals(480, res.configuration.mnc)
}
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index d4be3a26d958..feb99e6b248d 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -383,7 +383,7 @@ public class TetheringTest {
}
@Override
- public TetheringNotificationUpdater getNotificationUpdater(Context ctx) {
+ public TetheringNotificationUpdater getNotificationUpdater(Context ctx, Looper looper) {
return mNotificationUpdater;
}
}
@@ -1691,6 +1691,18 @@ public class TetheringTest {
assertEquals(clientAddrParceled, params.clientAddr);
}
+ @Test
+ public void testUpstreamNetworkChanged() {
+ final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
+ mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
+ final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ stateMachine.chooseUpstreamType(true);
+
+ verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
+ verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network));
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/read-snapshot.txt b/read-snapshot.txt
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/read-snapshot.txt
+++ /dev/null
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7230b00f87ad..f21f0e73e787 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -197,8 +197,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final MainHandler mMainHandler;
- // Lazily initialized - access through getSystemActionPerfomer()
- private SystemActionPerformer mSystemActionPerformer;
+ private final SystemActionPerformer mSystemActionPerformer;
private MagnificationController mMagnificationController;
@@ -296,6 +295,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
+ mSystemActionPerformer =
+ new SystemActionPerformer(mContext, mWindowManagerService, null, this);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
@@ -671,7 +672,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSecurityPolicy.enforceCallerIsRecentsOrHasPermission(
Manifest.permission.MANAGE_ACCESSIBILITY,
FUNCTION_REGISTER_SYSTEM_ACTION);
- getSystemActionPerformer().registerSystemAction(actionId, action);
+ mSystemActionPerformer.registerSystemAction(actionId, action);
}
/**
@@ -684,15 +685,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSecurityPolicy.enforceCallerIsRecentsOrHasPermission(
Manifest.permission.MANAGE_ACCESSIBILITY,
FUNCTION_UNREGISTER_SYSTEM_ACTION);
- getSystemActionPerformer().unregisterSystemAction(actionId);
- }
-
- private SystemActionPerformer getSystemActionPerformer() {
- if (mSystemActionPerformer == null) {
- mSystemActionPerformer =
- new SystemActionPerformer(mContext, mWindowManagerService, null, this);
- }
- return mSystemActionPerformer;
+ mSystemActionPerformer.unregisterSystemAction(actionId);
}
@Override
@@ -804,7 +797,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
- mSecurityPolicy, this, mWindowManagerService, getSystemActionPerformer(),
+ mSecurityPolicy, this, mWindowManagerService, mSystemActionPerformer,
mA11yWindowManager, flags);
onUserStateChangedLocked(getCurrentUserStateLocked());
}
@@ -1515,7 +1508,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (service == null) {
service = new AccessibilityServiceConnection(userState, mContext, componentName,
installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
- this, mWindowManagerService, getSystemActionPerformer(),
+ this, mWindowManagerService, mSystemActionPerformer,
mA11yWindowManager, mActivityTaskManagerService);
} else if (userState.mBoundServices.contains(service)) {
continue;
@@ -2441,7 +2434,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* accessibility button.
* 2) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
* version <= Q: turns on / off the accessibility service.
- * 3) For services targeting sdk version > Q:
+ * 3) For {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY} type and service targeting sdk
+ * version > Q and request accessibility button: turn on the accessibility service if it's
+ * not in the enabled state.
+ * (It'll happen when a service is disabled and assigned to shortcut then upgraded.)
+ * 4) For services targeting sdk version > Q:
* a) Turns on / off the accessibility service, if service does not request accessibility
* button.
* b) Callbacks to accessibility service if service is bounded and requests accessibility
@@ -2475,6 +2472,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
return true;
}
+ if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY && targetSdk > Build.VERSION_CODES.Q
+ && requestA11yButton) {
+ if (!userState.getEnabledServicesLocked().contains(assignedTarget)) {
+ enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
+ return true;
+ }
+ }
// Callbacks to a11y service if it's bounded and requests a11y button.
if (serviceConnection == null
|| !userState.mBoundServices.contains(serviceConnection)
@@ -2753,7 +2757,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState, mContext,
COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
AccessibilityManagerService.this, mWindowManagerService,
- getSystemActionPerformer(), mA11yWindowManager, mActivityTaskManagerService) {
+ mSystemActionPerformer, mA11yWindowManager, mActivityTaskManagerService) {
@Override
public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
return true;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a87fb8b5c301..b4f7cdbd5694 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -552,7 +552,8 @@ public final class DisplayManagerService extends SystemService {
}
if (state == Display.STATE_OFF) {
brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
- } else if (brightnessState < PowerManager.BRIGHTNESS_MIN || Float.isNaN(brightnessState)) {
+ } else if (brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT
+ && brightnessState < PowerManager.BRIGHTNESS_MIN) {
brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
} else if (brightnessState > PowerManager.BRIGHTNESS_MAX) {
brightnessState = PowerManager.BRIGHTNESS_MAX;
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c0502b8a068c..0b6024a84f78 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -211,24 +211,16 @@ public class UserRestrictionsUtils {
UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
- UserManager.DISALLOW_CONFIG_BLUETOOTH,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
- UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
UserManager.DISALLOW_CONFIG_TETHERING,
- UserManager.DISALLOW_CONFIG_WIFI,
- UserManager.DISALLOW_CONTENT_CAPTURE,
- UserManager.DISALLOW_CONTENT_SUGGESTIONS,
UserManager.DISALLOW_DATA_ROAMING,
- UserManager.DISALLOW_DEBUGGING_FEATURES,
UserManager.DISALLOW_SAFE_BOOT,
- UserManager.DISALLOW_SHARE_LOCATION,
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_USB_FILE_TRANSFER,
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
- UserManager.DISALLOW_OUTGOING_CALLS,
UserManager.DISALLOW_UNMUTE_MICROPHONE
);
@@ -237,7 +229,16 @@ public class UserRestrictionsUtils {
* set on the parent profile instance to apply them on the personal profile.
*/
private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS =
- Sets.newArraySet();
+ Sets.newArraySet(
+ UserManager.DISALLOW_CONFIG_BLUETOOTH,
+ UserManager.DISALLOW_CONFIG_LOCATION,
+ UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONTENT_CAPTURE,
+ UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_SHARE_LOCATION,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ );
/**
* User restrictions that default to {@code true} for managed profile owners.
diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index 67677c6cf17e..e1e6195ad260 100644
--- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -41,10 +41,11 @@ public final class ProcfsMemoryUtil {
public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
long[] output = new long[STATUS_KEYS.length];
output[0] = -1;
+ output[3] = -1;
+ output[4] = -1;
Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output);
- if (output[0] == -1 || (output[3] == 0 && output[4] == 0)) {
- // Could not open file or anon rss / swap are 0 indicating the process is in a zombie
- // state.
+ if (output[0] == -1 || output[3] == -1 || output[4] == -1) {
+ // Could not open or parse file.
return null;
}
final MemorySnapshot snapshot = new MemorySnapshot();
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 288c22a94b45..1afec9c18a7f 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2059,32 +2059,35 @@ public class StatsPullAtomService extends SystemService {
synchronized (mProcessStatsLock) {
final long token = Binder.clearCallingIdentity();
try {
+ // force procstats to flush & combine old files into one store
long lastHighWaterMark = readProcStatsHighWaterMark(section);
List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
- long highWaterMark = processStatsService.getCommittedStats(
- lastHighWaterMark, section, true, statsFiles);
- if (statsFiles.size() != 1) {
- return StatsManager.PULL_SKIP;
- }
- unpackStreamedData(atomTag, pulledData, statsFiles);
+
+ ProcessStats procStats = new ProcessStats(false);
+ long highWaterMark = processStatsService.getCommittedStatsMerged(
+ lastHighWaterMark, section, true, statsFiles, procStats);
+
+ // aggregate the data together for westworld consumption
+ ProtoOutputStream proto = new ProtoOutputStream();
+ procStats.dumpAggregatedProtoForStatsd(proto);
+
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeByteArray(proto.getBytes())
+ .build();
+ pulledData.add(e);
+
new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark)
.delete();
new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + highWaterMark)
.createNewFile();
- } catch (IOException e) {
- Slog.e(TAG, "Getting procstats failed: ", e);
- return StatsManager.PULL_SKIP;
- } catch (RemoteException e) {
- Slog.e(TAG, "Getting procstats failed: ", e);
- return StatsManager.PULL_SKIP;
- } catch (SecurityException e) {
+ } catch (RemoteException | IOException e) {
Slog.e(TAG, "Getting procstats failed: ", e);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
}
-
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
index 06c2354c7a7d..f59d431d4382 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
@@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.UserHandle;
+import android.text.TextUtils.SimpleStringSplitter;
import android.util.Log;
import android.util.Slog;
@@ -34,6 +35,8 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
/**
* Watches for emote provider services to be installed.
@@ -51,8 +54,8 @@ final class TvRemoteProviderWatcher {
private final PackageManager mPackageManager;
private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>();
private final int mUserId;
- private final String mUnbundledServicePackage;
private final Object mLock;
+ private final Set<String> mUnbundledServicePackages = new HashSet<>();
private boolean mRunning;
@@ -61,9 +64,19 @@ final class TvRemoteProviderWatcher {
mHandler = new Handler(true);
mUserId = UserHandle.myUserId();
mPackageManager = context.getPackageManager();
- mUnbundledServicePackage = context.getString(
- com.android.internal.R.string.config_tvRemoteServicePackage);
mLock = lock;
+
+ // Unbundled package names supports a comma-separated list
+ SimpleStringSplitter splitter = new SimpleStringSplitter(',');
+ splitter.setString(context.getString(
+ com.android.internal.R.string.config_tvRemoteServicePackage));
+
+ splitter.forEach(packageName -> {
+ packageName = packageName.trim();
+ if (!packageName.isEmpty()) {
+ mUnbundledServicePackages.add(packageName);
+ }
+ });
}
public void start() {
@@ -157,7 +170,7 @@ final class TvRemoteProviderWatcher {
}
// Check if package name is white-listed here.
- if (!serviceInfo.packageName.equals(mUnbundledServicePackage)) {
+ if (!mUnbundledServicePackages.contains(serviceInfo.packageName)) {
Slog.w(TAG, "Ignoring atv remote provider service because the package has not "
+ "been set and/or whitelisted: "
+ serviceInfo.packageName + "/" + serviceInfo.name);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7a04894523f5..d92f43b6890c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2786,6 +2786,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final int prevMode = task.getWindowingMode();
+ if (prevMode == windowingMode) {
+ // The task is already in split-screen and with correct windowing mode.
+ return true;
+ }
+
moveTaskToSplitScreenPrimaryTask(task, toTop);
return prevMode != task.getWindowingMode();
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f2d1a411c2c1..a47cdc66fbd8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3425,7 +3425,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mInputMethodTarget = target;
mInputMethodTargetWaitingAnim = targetWaitingAnim;
- assignWindowLayers(false /* setLayoutNeeded */);
+ assignWindowLayers(true /* setLayoutNeeded */);
updateImeParent();
updateImeControlTarget();
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ba14d48d38ea..4ac319ddf6ce 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -171,7 +171,7 @@ class InsetsStateController {
if (aboveIme) {
state = new InsetsState(state);
- state.removeSource(ITYPE_IME);
+ state.setSourceVisible(ITYPE_IME, false);
}
return state;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 0a9878dd660b..51053b2e7123 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -165,17 +165,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
- // TODO(multi-display) currently only support recents animation in default display.
- final DisplayContent dc =
- mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent;
- if (!mWindowManager.canStartRecentsAnimation()) {
- notifyAnimationCancelBeforeStart(recentsAnimationRunner);
- ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
- "Can't start recents animation, nextAppTransition=%s",
- dc.mAppTransition.getAppTransition());
- return;
- }
-
// If the activity is associated with the recents stack, then try and get that first
ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 84cc19d68a24..f55a1b3f6ab3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2797,18 +2797,6 @@ public class WindowManagerService extends IWindowManager.Stub
return mRecentsAnimationController;
}
- /**
- * @return Whether the next recents animation can continue to start. Called from
- * {@link RecentsAnimation#startRecentsActivity}.
- */
- boolean canStartRecentsAnimation() {
- // TODO(multi-display): currently only default display support recent activity
- if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
- return false;
- }
- return true;
- }
-
void cancelRecentsAnimation(
@RecentsAnimationController.ReorderMode int reorderMode, String reason) {
if (mRecentsAnimationController != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index af680701a33e..00c84ecb9f1f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1457,7 +1457,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void onDisplayChanged(DisplayContent dc) {
- if (dc != null && mDisplayContent != null
+ if (dc != null && mDisplayContent != null && dc != mDisplayContent
&& mDisplayContent.mInputMethodInputTarget == this) {
dc.setInputMethodInputTarget(mDisplayContent.mInputMethodInputTarget);
mDisplayContent.mInputMethodInputTarget = null;
@@ -5330,7 +5330,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// this promotion.
final WindowState imeTarget = getDisplayContent().mInputMethodTarget;
boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
- && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0;
+ && imeTarget.mToken == mToken
+ && getParent() != null
+ && imeTarget.compareTo(this) <= 0;
return inTokenWithAndAboveImeTarget;
}
return false;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4faed659f5df..09d1d3a270ba 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1986,29 +1986,32 @@ public class DevicePolicyManagerTest extends DpmTestBase {
Sets.newSet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
UserManager.DISALLOW_ADD_USER,
- UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
- UserManager.DISALLOW_CONFIG_BLUETOOTH,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
- UserManager.DISALLOW_CONFIG_LOCATION,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
UserManager.DISALLOW_CONFIG_PRIVATE_DNS,
UserManager.DISALLOW_CONFIG_TETHERING,
- UserManager.DISALLOW_CONFIG_WIFI,
- UserManager.DISALLOW_CONTENT_CAPTURE,
- UserManager.DISALLOW_CONTENT_SUGGESTIONS,
UserManager.DISALLOW_DATA_ROAMING,
- UserManager.DISALLOW_DEBUGGING_FEATURES,
UserManager.DISALLOW_SAFE_BOOT,
- UserManager.DISALLOW_SHARE_LOCATION,
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_USB_FILE_TRANSFER,
UserManager.DISALLOW_AIRPLANE_MODE,
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
- UserManager.DISALLOW_OUTGOING_CALLS,
UserManager.DISALLOW_UNMUTE_MICROPHONE
);
+ private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS =
+ Sets.newSet(
+ UserManager.DISALLOW_CONFIG_BLUETOOTH,
+ UserManager.DISALLOW_CONFIG_LOCATION,
+ UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONTENT_CAPTURE,
+ UserManager.DISALLOW_CONTENT_SUGGESTIONS,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_SHARE_LOCATION,
+ UserManager.DISALLOW_OUTGOING_CALLS
+ );
+
public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
final int MANAGED_PROFILE_ADMIN_UID =
UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
@@ -2021,7 +2024,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS) {
- addAndRemoveUserRestrictionOnParentDpm(restriction);
+ addAndRemoveGlobalUserRestrictionOnParentDpm(restriction);
+ }
+ for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS) {
+ addAndRemoveLocalUserRestrictionOnParentDpm(restriction);
}
parentDpm.setCameraDisabled(admin1, true);
@@ -2047,7 +2053,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
reset(getServices().userManagerInternal);
}
- private void addAndRemoveUserRestrictionOnParentDpm(String restriction) {
+ private void addAndRemoveGlobalUserRestrictionOnParentDpm(String restriction) {
parentDpm.addUserRestriction(admin1, restriction);
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(DpmMockContext.CALLER_USER_HANDLE),
@@ -2063,6 +2069,22 @@ public class DevicePolicyManagerTest extends DpmTestBase {
);
}
+ private void addAndRemoveLocalUserRestrictionOnParentDpm(String restriction) {
+ parentDpm.addUserRestriction(admin1, restriction);
+ verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(),
+ MockUtils.checkUserRestrictions(UserHandle.USER_SYSTEM, restriction),
+ eq(false));
+ parentDpm.clearUserRestriction(admin1, restriction);
+ DpmTestUtils.assertRestrictions(
+ DpmTestUtils.newRestrictions(),
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
+ );
+ }
+
public void testNoDefaultEnabledUserRestrictions() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index af89761acf9d..939b7a0beb49 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -35,6 +35,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseIntArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -542,7 +543,12 @@ public class PackageParserLegacyCoreTest {
@Test
public void testUsesSdk() throws Exception {
- parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+ ParsedPackage pkg =
+ parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+ SparseIntArray minExtVers = pkg.getMinExtensionVersions();
+ assertEquals(1, minExtVers.size());
+ assertEquals(0, minExtVers.get(10000, -1));
+
try {
parsePackage("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5, x -> x);
fail("Expected parsing exception due to incompatible extension SDK version");
diff --git a/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java b/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java
index 0a2bb620eb11..55e526f01aef 100644
--- a/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/TvRemoteProviderWatcherTest.java
@@ -84,6 +84,22 @@ public class TvRemoteProviderWatcherTest {
}
@Test
+ public void acceptsValidCsvPackageName() {
+ // Test intentionally includes empty spacing for a more complex test
+ when(mMockResources.getString(com.android.internal.R.string.config_tvRemoteServicePackage))
+ .thenReturn(",,foo, " + TV_REMOTE_SERVICE_PACKAGE_NAME + ",bar, baz,,");
+ assertTrue(mTvRemoteProviderWatcher.verifyServiceTrusted(createTvServiceInfo()));
+ }
+
+ @Test
+ public void rejectsInvalidCsvPackageName() {
+ // Checks include empty strings to validate that processing as well
+ when(mMockResources.getString(com.android.internal.R.string.config_tvRemoteServicePackage))
+ .thenReturn(",,foo,, ,bar, baz,,");
+ assertFalse(mTvRemoteProviderWatcher.verifyServiceTrusted(createTvServiceInfo()));
+ }
+
+ @Test
public void tvServiceIsTrusted() {
assertTrue(mTvRemoteProviderWatcher.verifyServiceTrusted(createTvServiceInfo()));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 61b74b0c1d0f..9f28f45a05d0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -29,9 +29,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
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.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.spy;
@@ -44,7 +46,6 @@ import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.test.InsetsModeSession;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.AfterClass;
@@ -153,22 +154,24 @@ public class InsetsStateControllerTest extends WindowTestsBase {
@Test
public void testStripForDispatch_belowIme() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ app.mBehindIme = true;
- assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
@Test
public void testStripForDispatch_aboveIme() {
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ app.mBehindIme = false;
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
@Test
@@ -188,11 +191,11 @@ public class InsetsStateControllerTest extends WindowTestsBase {
// Adding FLAG_NOT_FOCUSABLE makes app above IME.
app.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
mDisplayContent.computeImeTarget(true);
- mDisplayContent.setLayoutNeeded();
mDisplayContent.applySurfaceChangesTransaction();
- // app won't get IME insets while above IME.
- assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ // app won't get visible IME insets while above IME even when IME is visible.
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertFalse(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
// Reset invocation counter.
clearInvocations(app);
@@ -200,49 +203,49 @@ public class InsetsStateControllerTest extends WindowTestsBase {
// Removing FLAG_NOT_FOCUSABLE makes app below IME.
app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
mDisplayContent.computeImeTarget(true);
- mDisplayContent.setLayoutNeeded();
mDisplayContent.applySurfaceChangesTransaction();
// Make sure app got notified.
verify(app, atLeast(1)).notifyInsetsChanged();
- // app will get IME insets while below IME.
- assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+ // app will get visible IME insets while below IME when IME is visible.
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
}
@Test
public void testStripForDispatch_childWindow_altFocusable() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
-
- // IME cannot be the IME target.
- ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
-
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+ mDisplayContent.computeImeTarget(true);
+ mDisplayContent.setLayoutNeeded();
+ mDisplayContent.applySurfaceChangesTransaction();
- assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
}
@Test
public void testStripForDispatch_childWindow_splitScreen() {
- final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
-
- // IME cannot be the IME target.
- ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
-
- getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+ mDisplayContent.computeImeTarget(true);
+ mDisplayContent.setLayoutNeeded();
+ mDisplayContent.applySurfaceChangesTransaction();
- assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+ getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
+ assertTrue(getController().getInsetsForDispatch(app).getSource(ITYPE_IME).isVisible());
+ assertFalse(getController().getInsetsForDispatch(child).getSource(ITYPE_IME).isVisible());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 1f6ba7adf114..44ca2cdcb142 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -79,7 +79,6 @@ public class RecentsAnimationTest extends ActivityTestsBase {
mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
anyInt(), any(), any(), anyInt(), any(), any());
- doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
final RecentTasks recentTasks = mService.getRecentTasks();
spyOn(recentTasks);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index ecb80156aecc..5e8de8792cd1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -25,6 +25,8 @@ import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -60,7 +62,6 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.TextView;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.After;
@@ -78,8 +79,6 @@ import java.util.function.BooleanSupplier;
*/
// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
// TODO: Test non-Activity windows.
-@FlakyTest(detail = "TODO (b/145242835): Re-enable once type mapping is implemented for "
- + "PRIVATE_FLAG_IS_SCREEN_DECOR")
@SmallTest
@Presubmit
public class ScreenDecorWindowTests {
@@ -187,13 +186,24 @@ public class ScreenDecorWindowTests {
assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
}
+ @Test
+ public void testProvidesInsetsTypes() {
+ int[] providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+ final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED,
+ FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes);
+
+ assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
+ }
+
private View createDecorWindow(int gravity, int width, int height) {
+ int[] providesInsetsTypes =
+ new int[]{gravity == TOP ? ITYPE_STATUS_BAR : ITYPE_NAVIGATION_BAR};
return createWindow("decorWindow", gravity, width, height, RED,
- FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR);
+ FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes);
}
private View createWindow(String name, int gravity, int width, int height, int color, int flags,
- int privateFlags) {
+ int privateFlags, int[] providesInsetsTypes) {
final View[] viewHolder = new View[1];
final int finalFlag = flags
@@ -205,6 +215,7 @@ public class ScreenDecorWindowTests {
width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE);
lp.gravity = gravity;
lp.privateFlags |= privateFlags;
+ lp.providesInsetsTypes = providesInsetsTypes;
final TextView view = new TextView(mContext);
view.setText("ScreenDecorWindowTests - " + name);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 71b35b62366e..65fb2c0451d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -657,4 +657,16 @@ public class WindowStateTests extends WindowTestsBase {
win0.mActivityRecord.getStack().setFocusable(false);
assertTrue(win0.cantReceiveTouchInput());
}
+
+ @Test
+ public void testNeedsRelativeLayeringToIme_notAttached() {
+ WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken,
+ "SameTokenWindow");
+ mDisplayContent.mInputMethodTarget = mAppWindow;
+ sameTokenWindow.mActivityRecord.getStack().setWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertTrue(sameTokenWindow.needsRelativeLayeringToIme());
+ sameTokenWindow.removeImmediately();
+ assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
+ }
}
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a116c07e2646..242c2e979571 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -80,7 +80,6 @@ public final class DataCallResponse implements Parcelable {
private final int mMtu;
private final int mMtuV4;
private final int mMtuV6;
- private final int mVersion;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -126,9 +125,7 @@ public final class DataCallResponse implements Parcelable {
? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
mPcscfAddresses = (pcscfAddresses == null)
? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = mtu;
- mMtuV4 = mMtuV6 = 0;
- mVersion = 0;
+ mMtu = mMtuV4 = mMtuV6 = mtu;
}
/** @hide */
@@ -136,7 +133,7 @@ public final class DataCallResponse implements Parcelable {
@LinkStatus int linkStatus, @ProtocolType int protocolType,
@Nullable String interfaceName, @Nullable List<LinkAddress> addresses,
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
- @Nullable List<InetAddress> pcscfAddresses, int mtuV4, int mtuV6, int version) {
+ @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -151,10 +148,9 @@ public final class DataCallResponse implements Parcelable {
? new ArrayList<>() : new ArrayList<>(gatewayAddresses);
mPcscfAddresses = (pcscfAddresses == null)
? new ArrayList<>() : new ArrayList<>(pcscfAddresses);
- mMtu = 0;
+ mMtu = mtu;
mMtuV4 = mtuV4;
mMtuV6 = mtuV6;
- mVersion = version;
}
/** @hide */
@@ -177,7 +173,6 @@ public final class DataCallResponse implements Parcelable {
mMtu = source.readInt();
mMtuV4 = source.readInt();
mMtuV6 = source.readInt();
- mVersion = source.readInt();
}
/**
@@ -247,7 +242,7 @@ public final class DataCallResponse implements Parcelable {
*/
@Deprecated
public int getMtu() {
- return mVersion < 5 ? mMtu : 0;
+ return mMtu;
}
/**
@@ -256,7 +251,7 @@ public final class DataCallResponse implements Parcelable {
* Zero or negative values means network has either not sent a value or sent an invalid value.
*/
public int getMtuV4() {
- return mVersion < 5 ? 0 : mMtuV4;
+ return mMtuV4;
}
/**
@@ -264,7 +259,7 @@ public final class DataCallResponse implements Parcelable {
* Zero or negative values means network has either not sent a value or sent an invalid value.
*/
public int getMtuV6() {
- return mVersion < 5 ? 0 : mMtuV6;
+ return mMtuV6;
}
@NonNull
@@ -282,10 +277,9 @@ public final class DataCallResponse implements Parcelable {
.append(" dnses=").append(mDnsAddresses)
.append(" gateways=").append(mGatewayAddresses)
.append(" pcscf=").append(mPcscfAddresses)
- .append(" mtu=").append(mMtu)
- .append(" mtuV4=").append(mMtuV4)
- .append(" mtuV6=").append(mMtuV6)
- .append(" version=").append(mVersion)
+ .append(" mtu=").append(getMtu())
+ .append(" mtuV4=").append(getMtuV4())
+ .append(" mtuV6=").append(getMtuV6())
.append("}");
return sb.toString();
}
@@ -315,15 +309,14 @@ public final class DataCallResponse implements Parcelable {
&& mPcscfAddresses.containsAll(other.mPcscfAddresses)
&& mMtu == other.mMtu
&& mMtuV4 == other.mMtuV4
- && mMtuV6 == other.mMtuV6
- && mVersion == other.mVersion;
+ && mMtuV6 == other.mMtuV6;
}
@Override
public int hashCode() {
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
- mMtu, mMtuV4, mMtuV6, mVersion);
+ mMtu, mMtuV4, mMtuV6);
}
@Override
@@ -346,7 +339,6 @@ public final class DataCallResponse implements Parcelable {
dest.writeInt(mMtu);
dest.writeInt(mMtuV4);
dest.writeInt(mMtuV6);
- dest.writeInt(mVersion);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -403,8 +395,6 @@ public final class DataCallResponse implements Parcelable {
private int mMtuV6;
- private int mVersion;
-
/**
* Default constructor for Builder.
*/
@@ -563,29 +553,14 @@ public final class DataCallResponse implements Parcelable {
}
/**
- * Set the IRadio version for this DataCallResponse
- * @hide
- */
- public @NonNull Builder setVersion(int version) {
- mVersion = version;
- return this;
- }
-
- /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
*/
public @NonNull DataCallResponse build() {
- if (mVersion >= 5) {
- return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
- mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtuV4, mMtuV6, mVersion);
- } else {
- return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
- mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
- mPcscfAddresses, mMtu);
- }
+ return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
+ mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
+ mPcscfAddresses, mMtu, mMtuV4, mMtuV6);
}
}
}
diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml
index c6b4a54f3b0b..dd255ef5233b 100644
--- a/tests/UiBench/AndroidManifest.xml
+++ b/tests/UiBench/AndroidManifest.xml
@@ -306,5 +306,14 @@
<category android:name="com.android.test.uibench.TEST" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="WindowInsetsControllerActivity"
+ android:label="WindowInsetsControllerActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/UiBench/src/com/android/test/uibench/WindowInsetsControllerActivity.java b/tests/UiBench/src/com/android/test/uibench/WindowInsetsControllerActivity.java
new file mode 100644
index 000000000000..e4b89cdd5c8d
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/WindowInsetsControllerActivity.java
@@ -0,0 +1,53 @@
+/*
+ * 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.uibench;
+
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.widget.EditText;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.List;
+
+public class WindowInsetsControllerActivity extends AppCompatActivity {
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ EditText text = new EditText(this);
+ text.setText("WindowInsetsController");
+ setContentView(text);
+ getWindow().setDecorFitsSystemWindows(false);
+
+ text.setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @NonNull
+ @Override
+ public WindowInsets onProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return WindowInsets.CONSUMED;
+ }
+ });
+ }
+}
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 5fe94987aa65..b33995017bae 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -26,51 +26,14 @@ namespace android {
namespace stats_log_api_gen {
static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) {
- fprintf(out, "static int UNSET_VALUE = INT_MAX;\n");
- fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n");
-
- fprintf(out, "struct StateAtomFieldOptions {\n");
- fprintf(out, " std::vector<int> primaryFields;\n");
- fprintf(out, " int exclusiveField;\n");
- fprintf(out, " int defaultState = UNSET_VALUE;\n");
- fprintf(out, " int resetState = UNSET_VALUE;\n");
- fprintf(out, " bool nested;\n");
- fprintf(out, "};\n");
- fprintf(out, "\n");
-
fprintf(out, "struct AtomsInfo {\n");
- fprintf(out,
- " const static std::set<int> "
- "kTruncatingTimestampAtomBlackList;\n");
fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n");
- fprintf(out,
- " const static std::map<int, StateAtomFieldOptions> "
- "kStateAtomsFieldOptions;\n");
fprintf(out, " const static std::set<int> kWhitelistedAtoms;\n");
fprintf(out, "};\n");
fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId);
}
static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
- std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
- "audio_state_changed",
- "call_state_changed",
- "phone_signal_strength_changed",
- "mobile_bytes_transfer_by_fg_bg",
- "mobile_bytes_transfer"};
- fprintf(out,
- "const std::set<int> "
- "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- if (kTruncatingAtomNames.find((*atomIt)->name) != kTruncatingAtomNames.end()) {
- const string constant = make_constant_name((*atomIt)->name);
- fprintf(out, " %d, // %s\n", (*atomIt)->code, constant.c_str());
- }
- }
-
- fprintf(out, "};\n");
- fprintf(out, "\n");
fprintf(out, "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
@@ -100,49 +63,6 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
fprintf(out, "};\n");
fprintf(out, "\n");
- fprintf(out,
- "static std::map<int, StateAtomFieldOptions> "
- "getStateAtomFieldOptions() {\n");
- fprintf(out, " std::map<int, StateAtomFieldOptions> options;\n");
- fprintf(out, " StateAtomFieldOptions* opt;\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- if ((*atomIt)->primaryFields.size() == 0 && (*atomIt)->exclusiveField == 0) {
- continue;
- }
- fprintf(out,
- "\n // Adding primary and exclusive fields for atom "
- "(%d)%s\n",
- (*atomIt)->code, (*atomIt)->name.c_str());
- fprintf(out, " opt = &(options[%d /* %s */]);\n", (*atomIt)->code,
- make_constant_name((*atomIt)->name).c_str());
- fprintf(out, " opt->primaryFields.reserve(%lu);\n", (*atomIt)->primaryFields.size());
- for (const auto& field : (*atomIt)->primaryFields) {
- fprintf(out, " opt->primaryFields.push_back(%d);\n", field);
- }
-
- fprintf(out, " opt->exclusiveField = %d;\n", (*atomIt)->exclusiveField);
- if ((*atomIt)->defaultState != INT_MAX) {
- fprintf(out, " opt->defaultState = %d;\n", (*atomIt)->defaultState);
- } else {
- fprintf(out, " opt->defaultState = UNSET_VALUE;\n");
- }
-
- if ((*atomIt)->triggerStateReset != INT_MAX) {
- fprintf(out, " opt->resetState = %d;\n", (*atomIt)->triggerStateReset);
- } else {
- fprintf(out, " opt->resetState = UNSET_VALUE;\n");
- }
- fprintf(out, " opt->nested = %d;\n", (*atomIt)->nested);
- }
-
- fprintf(out, " return options;\n");
- fprintf(out, "}\n");
-
- fprintf(out,
- "const std::map<int, StateAtomFieldOptions> "
- "AtomsInfo::kStateAtomsFieldOptions = "
- "getStateAtomFieldOptions();\n");
}
int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) {
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index 2ecf3092035d..e55a89fddd0c 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -1,14 +1,41 @@
-# used by wifi-service
-# TODO (b/153596226): Find a solution for networkstack's AIDL parcelables & interfaces.
-# Parcelable class names are serialized in the wire, so renaming them
-# will result in the class not being found for any parcelable received/sent from the
-# wifi-service jar.
+## used by service-wifi ##
-# Note: This rule is needed to ensure the rule below does not rename a Parcelable (see TODO above).
-rule android.net.DhcpResultsParcelable* @0
+# Network Stack AIDL interface.
+rule android.net.DhcpResultsParcelable* com.android.wifi.x.@0
+rule android.net.IIpMemoryStore* com.android.wifi.x.@0
+rule android.net.IIpMemoryStoreCallbacks* com.android.wifi.x.@0
+rule android.net.INetd* com.android.wifi.x.@0
+rule android.net.INetdUnsolicitedEventListener* com.android.wifi.x.@0
+rule android.net.INetworkStackConnector* com.android.wifi.x.@0
+rule android.net.InformationElementParcelable* com.android.wifi.x.@0
+rule android.net.InitialConfigurationParcelable* com.android.wifi.x.@0
+rule android.net.InterfaceConfigurationParcel* com.android.wifi.x.@0
+rule android.net.Layer2InformationParcelable* com.android.wifi.x.@0
+rule android.net.Layer2PacketParcelable* com.android.wifi.x.@0
+rule android.net.MarkMaskParcel* com.android.wifi.x.@0
+rule android.net.NattKeepalivePacketDataParcelable* com.android.wifi.x.@0
+rule android.net.PrivateDnsConfigParcel* com.android.wifi.x.@0
+rule android.net.ProvisioningConfigurationParcelable* com.android.wifi.x.@0
+rule android.net.ResolverParamsParcel* com.android.wifi.x.@0
+rule android.net.RouteInfoParcel* com.android.wifi.x.@0
+rule android.net.ScanResultInfoParcelable* com.android.wifi.x.@0
+rule android.net.TetherConfigParcel* com.android.wifi.x.@0
+rule android.net.TetherOffloadRuleParcel* com.android.wifi.x.@0
+rule android.net.TetherStatsParcel* com.android.wifi.x.@0
+rule android.net.UidRangeParcel* com.android.wifi.x.@0
+rule android.net.dhcp.DhcpLeaseParcelable* com.android.wifi.x.@0
+rule android.net.dhcp.DhcpServingParamsParcel* com.android.wifi.x.@0
+rule android.net.ip.IIpClient* com.android.wifi.x.@0
+rule android.net.ip.IIpClientCallbacks* com.android.wifi.x.@0
+rule android.net.ipmemorystore.Blob* com.android.wifi.x.@0
+rule android.net.ipmemorystore.IOnBlobRetrievedListener* com.android.wifi.x.@0
+rule android.net.ipmemorystore.IOnStatusListener* com.android.wifi.x.@0
+rule android.net.ipmemorystore.NetworkAttributesParcelable* com.android.wifi.x.@0
+rule android.net.ipmemorystore.SameL3NetworkResponseParcelable* com.android.wifi.x.@0
+rule android.net.ipmemorystore.StatusParcelable* com.android.wifi.x.@0
+
+# Net utils (includes Network Stack helper classes).
rule android.net.DhcpResults* com.android.wifi.x.@0
-# Note: This rule is needed to ensure the rule below does not rename a Parcelable (see TODO above).
-rule android.net.InterfaceConfigurationParcel* @0
rule android.net.InterfaceConfiguration* com.android.wifi.x.@0
rule android.net.IpMemoryStore* com.android.wifi.x.@0
rule android.net.NetworkMonitorManager* com.android.wifi.x.@0
@@ -19,8 +46,6 @@ rule android.net.ip.IpClientManager* com.android.wifi.x.@0
rule android.net.ip.IpClientUtil* com.android.wifi.x.@0
rule android.net.ipmemorystore.OnBlobRetrievedListener* com.android.wifi.x.@0
rule android.net.ipmemorystore.OnStatusListener* com.android.wifi.x.@0
-# Note: This rule is needed to ensure the rule below does not rename a Parcelable (see TODO above).
-rule android.net.ipmemorystore.StatusParcelable* @0
rule android.net.ipmemorystore.Status* com.android.wifi.x.@0
rule android.net.networkstack.ModuleNetworkStackClient* com.android.wifi.x.@0
rule android.net.networkstack.NetworkStackClientBase* com.android.wifi.x.@0
@@ -81,7 +106,7 @@ rule org.ksoap2.** com.android.wifi.x.@0
# Use our statically linked nanohttpd
rule fi.iki.elonen.** com.android.wifi.x.@0
-# used by both framework-wifi and wifi-service
+## used by both framework-wifi and service-wifi ##
rule android.content.pm.BaseParceledListSlice* com.android.wifi.x.@0
rule android.content.pm.ParceledListSlice* com.android.wifi.x.@0
rule android.net.shared.Inet4AddressUtils* com.android.wifi.x.@0