summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/statsd/src/FieldValue.h12
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp22
-rw-r--r--cmds/statsd/src/external/StatsCallbackPuller.cpp10
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.cpp46
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h20
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp17
-rw-r--r--cmds/statsd/src/logd/LogEvent.h33
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp2
-rw-r--r--cmds/statsd/src/stats_log.proto2
-rw-r--r--cmds/statsd/src/stats_log_util.cpp3
-rw-r--r--cmds/statsd/tests/LogEvent_test.cpp2
-rw-r--r--cmds/statsd/tests/guardrail/StatsdStats_test.cpp35
-rw-r--r--core/java/android/app/ActivityTaskManager.java12
-rw-r--r--core/java/android/view/WindowManager.java41
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java79
-rw-r--r--core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java8
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java177
-rw-r--r--core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java12
-rw-r--r--core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java4
-rw-r--r--media/java/android/media/AudioPortEventHandler.java3
-rw-r--r--media/java/android/media/MediaCas.java20
-rw-r--r--media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java3
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl10
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java15
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml2
-rw-r--r--packages/CarSystemUI/res/values/config.xml18
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java10
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java9
-rw-r--r--packages/SystemUI/res/drawable/bubble_manage_menu_row.xml21
-rw-r--r--packages/SystemUI/res/layout/bubble_manage_menu.xml99
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java96
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java216
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java119
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java156
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java68
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java2
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java30
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java153
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java12
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java117
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java40
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java152
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java28
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java285
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java15
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java20
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java219
-rwxr-xr-xtelecomm/java/android/telecom/Connection.java25
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java44
-rw-r--r--telecomm/java/android/telecom/Logging/Session.java2
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java20
84 files changed, 2260 insertions, 914 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index 755380e93b2a..777cbc540b54 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -126,6 +126,7 @@ package android.app {
public class ActivityTaskManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
+ method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public String listAllStacks();
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToStack(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index e251399776fb..ba4cf11b84f1 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -382,10 +382,6 @@ public:
inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
- inline void setResetState(int32_t resetState) {
- mResetState = resetState;
- }
-
// Default value = false
inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
@@ -398,12 +394,6 @@ public:
// Default value = false
inline bool isUidField() const { return getValueFromBitmask(UID_POS); }
- // 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 int32_t getResetState() const {
- return mResetState;
- }
-
private:
inline void setBitmaskAtPos(int pos, bool value) {
mBooleanBitmask &= ~(1 << pos); // clear
@@ -417,8 +407,6 @@ private:
// This is a bitmask over all annotations stored in boolean form. Because
// there are only 4 booleans, just one byte is required.
uint8_t mBooleanBitmask = 0;
-
- int32_t mResetState = -1;
};
/**
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index d914ab2436c7..f91c6002bf5e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -389,15 +389,24 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) {
void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ // Tell StatsdStats about new event
+ const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs();
+ int atomId = event->GetTagId();
+ StatsdStats::getInstance().noteAtomLogged(atomId, eventElapsedTimeNs / NS_PER_SEC);
+ if (!event->isValid()) {
+ StatsdStats::getInstance().noteAtomError(atomId);
+ return;
+ }
+
// Hard-coded logic to update train info on disk and fill in any information
// this log event may be missing.
- if (event->GetTagId() == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) {
+ if (atomId == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) {
onBinaryPushStateChangedEventLocked(event);
}
// Hard-coded logic to update experiment ids on disk for certain rollback
// types and fill the rollback atom with experiment ids
- if (event->GetTagId() == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) {
+ if (atomId == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) {
onWatchdogRollbackOccurredLocked(event);
}
@@ -406,16 +415,11 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
ALOGI("%s", event->ToString().c_str());
}
#endif
- const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs();
-
resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
- StatsdStats::getInstance().noteAtomLogged(
- event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
-
// Hard-coded logic to update the isolated uid's in the uid-map.
// The field numbers need to be currently updated by hand with atoms.proto
- if (event->GetTagId() == android::os::statsd::util::ISOLATED_UID_CHANGED) {
+ if (atomId == android::os::statsd::util::ISOLATED_UID_CHANGED) {
onIsolatedUidChangedEventLocked(*event);
}
@@ -432,7 +436,7 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
}
- if (event->GetTagId() != android::os::statsd::util::ISOLATED_UID_CHANGED) {
+ if (atomId != android::os::statsd::util::ISOLATED_UID_CHANGED) {
// Map the isolated uid to host uid if necessary.
mapIsolatedUidToHostUidIfNecessaryLocked(event);
}
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 933f48d1714b..3618bb0dd08b 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -67,8 +67,14 @@ bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
lock_guard<mutex> lk(*cv_mutex);
for (const StatsEventParcel& parcel: output) {
shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
- event->parseBuffer((uint8_t*)parcel.buffer.data(), parcel.buffer.size());
- sharedData->push_back(event);
+ bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(),
+ parcel.buffer.size());
+ if (valid) {
+ sharedData->push_back(event);
+ } else {
+ StatsdStats::getInstance().noteAtomError(event->GetTagId(),
+ /*pull=*/true);
+ }
}
*pullSuccess = success;
*pullFinish = true;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index db637b15e969..46f5dbda5521 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -54,6 +54,7 @@ const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19;
const int FIELD_ID_ATOM_STATS_TAG = 1;
const int FIELD_ID_ATOM_STATS_COUNT = 2;
+const int FIELD_ID_ATOM_STATS_ERROR_COUNT = 3;
const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1;
@@ -549,6 +550,20 @@ void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayN
std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
}
+void StatsdStats::noteAtomError(int atomTag, bool pull) {
+ lock_guard<std::mutex> lock(mLock);
+ if (pull) {
+ mPulledAtomStats[atomTag].atomErrorCount++;
+ return;
+ }
+
+ bool present = (mPushedAtomErrorStats.find(atomTag) != mPushedAtomErrorStats.end());
+ bool full = (mPushedAtomErrorStats.size() >= (size_t)kMaxPushedAtomErrorStatsSize);
+ if (!full || present) {
+ mPushedAtomErrorStats[atomTag]++;
+ }
+}
+
StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) {
auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
if (atomMetricStatsIter != mAtomMetricStats.end()) {
@@ -604,9 +619,11 @@ void StatsdStats::resetInternalLocked() {
pullStats.second.pullExceedMaxDelay = 0;
pullStats.second.registeredCount = 0;
pullStats.second.unregisteredCount = 0;
+ pullStats.second.atomErrorCount = 0;
}
mAtomMetricStats.clear();
mActivationBroadcastGuardrailStats.clear();
+ mPushedAtomErrorStats.clear();
}
string buildTimeString(int64_t timeSec) {
@@ -617,6 +634,15 @@ string buildTimeString(int64_t timeSec) {
return string(timeBuffer);
}
+int StatsdStats::getPushedAtomErrors(int atomId) const {
+ const auto& it = mPushedAtomErrorStats.find(atomId);
+ if (it != mPushedAtomErrorStats.end()) {
+ return it->second;
+ } else {
+ return 0;
+ }
+}
+
void StatsdStats::dumpStats(int out) const {
lock_guard<std::mutex> lock(mLock);
time_t t = mStartTimeSec;
@@ -721,11 +747,13 @@ void StatsdStats::dumpStats(int out) const {
const size_t atomCounts = mPushedAtomStats.size();
for (size_t i = 2; i < atomCounts; i++) {
if (mPushedAtomStats[i] > 0) {
- dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+ dprintf(out, "Atom %zu->(total count)%d, (error count)%d\n", i, mPushedAtomStats[i],
+ getPushedAtomErrors((int)i));
}
}
for (const auto& pair : mNonPlatformPushedAtomStats) {
- dprintf(out, "Atom %lu->%d\n", (unsigned long)pair.first, pair.second);
+ dprintf(out, "Atom %d->(total count)%d, (error count)%d\n", pair.first, pair.second,
+ getPushedAtomErrors(pair.first));
}
dprintf(out, "********Pulled Atom stats***********\n");
@@ -737,13 +765,15 @@ void StatsdStats::dumpStats(int out) const {
"nanos)%lld, "
" (max pull delay nanos)%lld, (data error)%ld\n"
" (pull timeout)%ld, (pull exceed max delay)%ld\n"
- " (registered count) %ld, (unregistered count) %ld\n",
+ " (registered count) %ld, (unregistered count) %ld\n"
+ " (atom error count) %d\n",
(int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
(long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec,
(long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs,
(long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs,
pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
- pair.second.registeredCount, pair.second.unregisteredCount);
+ pair.second.registeredCount, pair.second.unregisteredCount,
+ pair.second.atomErrorCount);
}
if (mAnomalyAlarmRegisteredStats > 0) {
@@ -919,6 +949,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
+ int errors = getPushedAtomErrors(i);
+ if (errors > 0) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
+ }
proto.end(token);
}
}
@@ -928,6 +962,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second);
+ int errors = getPushedAtomErrors(pair.first);
+ if (errors > 0) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
+ }
proto.end(token);
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index ff31e9e73fd0..21e524a9fadf 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -461,6 +461,16 @@ public:
*/
void noteActivationBroadcastGuardrailHit(const int uid);
+ /**
+ * Reports that an atom is erroneous or cannot be parsed successfully by
+ * statsd. An atom tag of 0 indicates that the client did not supply the
+ * atom id within the encoding.
+ *
+ * For pushed atoms only, this call should be preceded by a call to
+ * noteAtomLogged.
+ */
+ void noteAtomError(int atomTag, bool pull=false);
+
/**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
@@ -499,6 +509,7 @@ public:
long emptyData = 0;
long registeredCount = 0;
long unregisteredCount = 0;
+ int32_t atomErrorCount = 0;
} PulledAtomStats;
typedef struct {
@@ -546,6 +557,12 @@ private:
// Maps PullAtomId to its stats. The size is capped by the puller atom counts.
std::map<int, PulledAtomStats> mPulledAtomStats;
+ // Stores the number of times a pushed atom was logged erroneously. The
+ // corresponding counts for pulled atoms are stored in PulledAtomStats.
+ // The max size of this map is kMaxAtomErrorsStatsSize.
+ std::map<int, int> mPushedAtomErrorStats;
+ int kMaxPushedAtomErrorStatsSize = 100;
+
// Maps metric ID to its stats. The size is capped by the number of metrics.
std::map<int64_t, AtomMetricStats> mAtomMetricStats;
@@ -613,6 +630,8 @@ private:
void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
+ int getPushedAtomErrors(int atomId) const;
+
/**
* Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
* will live as long as `this`.
@@ -631,6 +650,7 @@ private:
FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
+ FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
};
} // namespace statsd
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 61cd01728ab1..eb830e114b40 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -114,14 +114,6 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
}
-LogEvent::~LogEvent() {
- if (mContext) {
- // This is for the case when LogEvent is created using the test interface
- // but init() isn't called.
- android_log_destroy(&mContext);
- }
-}
-
void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t value = readNextValue<int32_t>();
addToValues(pos, depth, value, last);
@@ -303,8 +295,7 @@ void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
return;
}
- int32_t resetState = readNextValue<int32_t>();
- mValues[mValues.size() - 1].mAnnotations.setResetState(resetState);
+ mResetState = readNextValue<int32_t>();
}
void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
@@ -386,7 +377,6 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
typeInfo = readNextValue<uint8_t>();
uint8_t typeId = getTypeId(typeInfo);
- // TODO(b/144373276): handle errors passed to the socket
switch (typeId) {
case BOOL_TYPE:
parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
@@ -413,8 +403,13 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
if (mAttributionChainIndex == -1) mAttributionChainIndex = pos[0];
break;
+ case ERROR_TYPE:
+ mErrorBitmask = readNextValue<int32_t>();
+ mValid = false;
+ break;
default:
mValid = false;
+ break;
}
}
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 41fdcc2cbe7a..dedcfaf6cd87 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -70,7 +70,7 @@ public:
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& installTrainInfo);
- ~LogEvent();
+ ~LogEvent() {}
/**
* Get the timestamp associated with this event.
@@ -184,6 +184,12 @@ public:
return mExclusiveStateFieldIndex;
}
+ // If a reset state is not sent in the StatsEvent, returns -1. Note that a
+ // reset state is sent if and only if a reset should be triggered.
+ inline int getResetState() const {
+ return mResetState;
+ }
+
inline LogEvent makeCopy() {
return LogEvent(*this);
}
@@ -204,6 +210,14 @@ public:
return BAD_INDEX;
}
+ bool isValid() const {
+ return mValid;
+ }
+
+ int32_t getErrorBitmask() const {
+ return mErrorBitmask;
+ }
+
private:
/**
* Only use this if copy is absolutely needed.
@@ -230,12 +244,13 @@ private:
bool checkPreviousValueType(Type expected);
/**
- * The below three variables are only valid during the execution of
+ * The below two variables are only valid during the execution of
* parseBuffer. There are no guarantees about the state of these variables
* before/after.
*/
uint8_t* mBuf;
uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
+
bool mValid = true; // stores whether the event we received from the socket is valid
/**
@@ -287,19 +302,15 @@ private:
// matching.
std::vector<FieldValue> mValues;
- // This field is used when statsD wants to create log event object and write fields to it. After
- // calling init() function, this object would be destroyed to save memory usage.
- // When the log event is created from log msg, this field is never initiated.
- android_log_context mContext = NULL;
-
// The timestamp set by the logd.
int64_t mLogdTimestampNs;
// The elapsed timestamp set by statsd log writer.
int64_t mElapsedTimestampNs;
- // The atom tag of the event.
- int mTagId;
+ // The atom tag of the event (defaults to 0 if client does not
+ // appropriately set the atom id).
+ int mTagId = 0;
// The uid of the logging client (defaults to -1).
int32_t mLogUid = -1;
@@ -307,11 +318,15 @@ private:
// The pid of the logging client (defaults to -1).
int32_t mLogPid = -1;
+ // Bitmask of errors sent by StatsEvent/AStatsEvent.
+ int32_t mErrorBitmask = 0;
+
// Annotations
bool mTruncateTimestamp = false;
int mUidFieldIndex = -1;
int mAttributionChainIndex = -1;
int mExclusiveStateFieldIndex = -1;
+ int mResetState = -1;
};
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index b7f314a819df..b63713b64c5d 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -51,7 +51,7 @@ void StateTracker::onLogEvent(const LogEvent& event) {
return;
}
- const int32_t resetState = stateValue.mAnnotations.getResetState();
+ const int32_t resetState = event.getResetState();
if (resetState != -1) {
VLOG("StateTracker new reset state: %d", resetState);
handleReset(eventTimeNs, resetState);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index f4247eca6e23..868247bc9d64 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -425,6 +425,7 @@ message StatsdStatsReport {
message AtomStats {
optional int32 tag = 1;
optional int32 count = 2;
+ optional int32 error_count = 3;
}
repeated AtomStats atom_stats = 7;
@@ -460,6 +461,7 @@ message StatsdStatsReport {
optional int64 empty_data = 15;
optional int64 registered_count = 16;
optional int64 unregistered_count = 17;
+ optional int32 atom_error_count = 18;
}
repeated PulledAtomStats pulled_atom_stats = 10;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 563531351fe0..f9fddc8a7c8a 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -80,6 +80,8 @@ const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14;
const int FIELD_ID_EMPTY_DATA = 15;
const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
+const int FIELD_ID_ATOM_ERROR_COUNT = 18;
+
// for AtomMetricStats proto
const int FIELD_ID_ATOM_METRIC_STATS = 17;
const int FIELD_ID_METRIC_ID = 1;
@@ -492,6 +494,7 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>
(long long) pair.second.registeredCount);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
(long long) pair.second.unregisteredCount);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
protoOutput->end(token);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index bb4578d9b701..e52e2d024e94 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -352,7 +352,7 @@ TEST(LogEventTest, TestResetStateAnnotation) {
const vector<FieldValue>& values = event.getValues();
EXPECT_EQ(values.size(), 1);
- EXPECT_EQ(values[0].mAnnotations.getResetState(), resetState);
+ EXPECT_EQ(event.getResetState(), resetState);
}
} // namespace statsd
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 129fafa75b41..cdde603f4c0b 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -486,6 +486,41 @@ TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit) {
EXPECT_TRUE(uid2Good);
}
+TEST(StatsdStatsTest, TestAtomErrorStats) {
+ StatsdStats stats;
+
+ int pushAtomTag = 100;
+ int pullAtomTag = 1000;
+ int numErrors = 10;
+
+ for (int i = 0; i < numErrors; i++) {
+ // We must call noteAtomLogged as well because only those pushed atoms
+ // that have been logged will have stats printed about them in the
+ // proto.
+ stats.noteAtomLogged(pushAtomTag, /*timeSec=*/0);
+ stats.noteAtomError(pushAtomTag, /*pull=*/false);
+
+ stats.noteAtomError(pullAtomTag, /*pull=*/true);
+ }
+
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false);
+ StatsdStatsReport report;
+ EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
+
+ // Check error count = numErrors for push atom
+ EXPECT_EQ(1, report.atom_stats_size());
+ const auto& pushedAtomStats = report.atom_stats(0);
+ EXPECT_EQ(pushAtomTag, pushedAtomStats.tag());
+ EXPECT_EQ(numErrors, pushedAtomStats.error_count());
+
+ // Check error count = numErrors for pull atom
+ EXPECT_EQ(1, report.pulled_atom_stats_size());
+ const auto& pulledAtomStats = report.pulled_atom_stats(0);
+ EXPECT_EQ(pullAtomTag, pulledAtomStats.atom_id());
+ EXPECT_EQ(numErrors, pulledAtomStats.atom_error_count());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 1b941defba42..1cc63da3db0a 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -23,8 +23,10 @@ import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -430,4 +432,14 @@ public class ActivityTaskManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /** Returns whether the current UI mode supports error dialogs (ANR, crash, etc). */
+ public static boolean currentUiModeSupportsErrorDialogs(@NonNull Context context) {
+ final Configuration config = context.getResources().getConfiguration();
+ int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
+ return (modeType != Configuration.UI_MODE_TYPE_CAR
+ && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
+ && modeType != Configuration.UI_MODE_TYPE_TELEVISION
+ && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cc380f32297e..e4dbd63765b6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -514,33 +514,24 @@ public interface WindowManager extends ViewManager {
int TAKE_SCREENSHOT_PROVIDED_IMAGE = 3;
/**
- * Parcel key for the screen shot bitmap sent with messages of type
- * {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}, type {@link android.graphics.Bitmap}
- * @hide
- */
- String PARCEL_KEY_SCREENSHOT_BITMAP = "screenshot_screen_bitmap";
-
- /**
- * Parcel key for the screen bounds of the image sent with messages of type
- * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link Rect} in screen coordinates.
- * @hide
- */
- String PARCEL_KEY_SCREENSHOT_BOUNDS = "screenshot_screen_bounds";
-
- /**
- * Parcel key for the task id of the task that the screen shot was taken of, sent with messages
- * of type [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type int.
- * @hide
- */
- String PARCEL_KEY_SCREENSHOT_TASK_ID = "screenshot_task_id";
-
- /**
- * Parcel key for the visible insets of the image sent with messages of type
- * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link android.graphics.Insets} in
- * screen coordinates.
+ * Enum listing the possible sources from which a screenshot was originated. Used for logging.
+ *
* @hide
*/
- String PARCEL_KEY_SCREENSHOT_INSETS = "screenshot_insets";
+ @IntDef({ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS,
+ ScreenshotSource.SCREENSHOT_KEY_CHORD,
+ ScreenshotSource.SCREENSHOT_KEY_OTHER,
+ ScreenshotSource.SCREENSHOT_OVERVIEW,
+ ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS,
+ ScreenshotSource.SCREENSHOT_OTHER})
+ @interface ScreenshotSource {
+ int SCREENSHOT_GLOBAL_ACTIONS = 0;
+ int SCREENSHOT_KEY_CHORD = 1;
+ int SCREENSHOT_KEY_OTHER = 2;
+ int SCREENSHOT_OVERVIEW = 3;
+ int SCREENSHOT_ACCESSIBILITY_ACTIONS = 4;
+ int SCREENSHOT_OTHER = 5;
+ }
/**
* @hide
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 3eb0923363f2..2c4892561ddd 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -50,6 +50,9 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/**
* This is used in conjunction with
@@ -74,11 +77,13 @@ public class IntentForwarderActivity extends Activity {
private Injector mInjector;
private MetricsLogger mMetricsLogger;
+ protected ExecutorService mExecutorService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInjector = createInjector();
+ mExecutorService = Executors.newSingleThreadExecutor();
Intent intentReceived = getIntent();
String className = intentReceived.getComponent().getClassName();
@@ -118,30 +123,9 @@ public class IntentForwarderActivity extends Activity {
mInjector.getIPackageManager(), getContentResolver());
if (newIntent != null) {
newIntent.prepareToLeaveUser(callingUserId);
-
- final ResolveInfo ri = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY,
- targetUserId);
- try {
- startActivityAsCaller(newIntent, null, null, false, targetUserId);
- } catch (RuntimeException e) {
- int launchedFromUid = -1;
- String launchedFromPackage = "?";
- try {
- launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid(
- getActivityToken());
- launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage(
- getActivityToken());
- } catch (RemoteException ignored) {
- }
-
- Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package "
- + launchedFromPackage + ", while running in "
- + ActivityThread.currentProcessName(), e);
- }
-
- if (shouldShowDisclosure(ri, intentReceived)) {
- mInjector.showToast(userMessageId, Toast.LENGTH_LONG);
- }
+ maybeShowDisclosureAsync(intentReceived, newIntent, targetUserId, userMessageId);
+ CompletableFuture.runAsync(() -> startActivityAsCaller(
+ newIntent, targetUserId), mExecutorService);
} else {
Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user "
+ callingUserId + " to user " + targetUserId);
@@ -149,6 +133,44 @@ public class IntentForwarderActivity extends Activity {
finish();
}
+ private void maybeShowDisclosureAsync(
+ Intent intentReceived, Intent newIntent, int userId, int messageId) {
+ final CompletableFuture<ResolveInfo> resolveInfoFuture =
+ mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY, userId);
+ resolveInfoFuture.thenAcceptAsync(ri -> {
+ if (shouldShowDisclosure(ri, intentReceived)) {
+ mInjector.showToast(messageId, Toast.LENGTH_LONG);
+ }
+ }, getApplicationContext().getMainExecutor());
+ }
+
+ private void startActivityAsCaller(Intent newIntent, int userId) {
+ try {
+ startActivityAsCaller(
+ newIntent,
+ /* options= */ null,
+ /* permissionToken= */ null,
+ /* ignoreTargetSecurity= */ false,
+ userId);
+ } catch (RuntimeException e) {
+ int launchedFromUid = -1;
+ String launchedFromPackage = "?";
+ try {
+ launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid(
+ getActivityToken());
+ launchedFromPackage = ActivityTaskManager.getService()
+ .getLaunchedFromPackage(getActivityToken());
+ } catch (RemoteException ignored) {
+ }
+
+ Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package "
+ + launchedFromPackage + ", while running in "
+ + ActivityThread.currentProcessName(), e);
+ } finally {
+ mExecutorService.shutdown();
+ }
+ }
+
private void launchChooserActivityWithCorrectTab(Intent intentReceived, String className) {
// When showing the sharesheet, instead of forwarding to the other profile,
// we launch the sharesheet in the current user and select the other tab.
@@ -322,8 +344,11 @@ public class IntentForwarderActivity extends Activity {
}
@Override
- public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
- return getPackageManager().resolveActivityAsUser(intent, flags, userId);
+ @Nullable
+ public CompletableFuture<ResolveInfo> resolveActivityAsUser(
+ Intent intent, int flags, int userId) {
+ return CompletableFuture.supplyAsync(
+ () -> getPackageManager().resolveActivityAsUser(intent, flags, userId));
}
@Override
@@ -339,7 +364,7 @@ public class IntentForwarderActivity extends Activity {
PackageManager getPackageManager();
- ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
+ CompletableFuture<ResolveInfo> resolveActivityAsUser(Intent intent, int flags, int userId);
void showToast(@StringRes int messageId, int duration);
}
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
index ebfea450af88..56a6db95badc 100644
--- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -46,6 +46,9 @@ public class GestureNavigationSettingsObserver extends ContentObserver {
r.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
false, this, UserHandle.USER_ALL);
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+ false, this, UserHandle.USER_ALL);
}
public void unregister() {
@@ -68,6 +71,11 @@ public class GestureNavigationSettingsObserver extends ContentObserver {
return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT);
}
+ public boolean areNavigationButtonForcedVisible() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) == 0;
+ }
+
private int getSensitivity(Resources userRes, String side) {
final int inset = userRes.getDimensionPixelSize(
com.android.internal.R.dimen.config_backGestureInset);
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 7cff90bbf437..adadc5e20549 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,5 +1,7 @@
package com.android.internal.util;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -10,11 +12,12 @@ import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
@@ -23,6 +26,109 @@ import android.view.WindowManager;
import java.util.function.Consumer;
public class ScreenshotHelper {
+
+ /**
+ * Describes a screenshot request (to make it easier to pass data through to the handler).
+ */
+ public static class ScreenshotRequest implements Parcelable {
+ private int mSource;
+ private boolean mHasStatusBar;
+ private boolean mHasNavBar;
+ private Bitmap mBitmap;
+ private Rect mBoundsInScreen;
+ private Insets mInsets;
+ private int mTaskId;
+
+ ScreenshotRequest(int source, boolean hasStatus, boolean hasNav) {
+ mSource = source;
+ mHasStatusBar = hasStatus;
+ mHasNavBar = hasNav;
+ }
+
+ ScreenshotRequest(
+ int source, Bitmap bitmap, Rect boundsInScreen, Insets insets, int taskId) {
+ mSource = source;
+ mBitmap = bitmap;
+ mBoundsInScreen = boundsInScreen;
+ mInsets = insets;
+ mTaskId = taskId;
+ }
+
+ ScreenshotRequest(Parcel in) {
+ mSource = in.readInt();
+ mHasStatusBar = in.readBoolean();
+ mHasNavBar = in.readBoolean();
+ if (in.readInt() == 1) {
+ mBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader());
+ mInsets = in.readParcelable(Insets.class.getClassLoader());
+ mTaskId = in.readInt();
+ }
+ }
+
+ public int getSource() {
+ return mSource;
+ }
+
+ public boolean getHasStatusBar() {
+ return mHasStatusBar;
+ }
+
+ public boolean getHasNavBar() {
+ return mHasNavBar;
+ }
+
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+
+ public Rect getBoundsInScreen() {
+ return mBoundsInScreen;
+ }
+
+ public Insets getInsets() {
+ return mInsets;
+ }
+
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSource);
+ dest.writeBoolean(mHasStatusBar);
+ dest.writeBoolean(mHasNavBar);
+ if (mBitmap == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeParcelable(mBitmap, 0);
+ dest.writeParcelable(mBoundsInScreen, 0);
+ dest.writeParcelable(mInsets, 0);
+ dest.writeInt(mTaskId);
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<ScreenshotRequest> CREATOR =
+ new Parcelable.Creator<ScreenshotRequest>() {
+
+ @Override
+ public ScreenshotRequest createFromParcel(Parcel source) {
+ return new ScreenshotRequest(source);
+ }
+
+ @Override
+ public ScreenshotRequest[] newArray(int size) {
+ return new ScreenshotRequest[size];
+ }
+ };
+ }
private static final String TAG = "ScreenshotHelper";
// Time until we give up on the screenshot & show an error instead.
@@ -36,8 +142,10 @@ public class ScreenshotHelper {
mContext = context;
}
+
+
/**
- * Request a screenshot be taken with a specific timeout.
+ * Request a screenshot be taken.
*
* Added to support reducing unit test duration; the method variant without a timeout argument
* is recommended for general use.
@@ -47,6 +155,32 @@ public class ScreenshotHelper {
* or
* {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
* @param hasStatus {@code true} if the status bar is currently showing. {@code false}
+ * if not.
+ * @param hasNav {@code true} if the navigation bar is currently showing. {@code
+ * false} if not.
+ * @param source The source of the screenshot request. One of
+ * {SCREENSHOT_GLOBAL_ACTIONS, SCREENSHOT_KEY_CHORD,
+ * SCREENSHOT_OVERVIEW, SCREENSHOT_OTHER}
+ * @param handler A handler used in case the screenshot times out
+ * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the
+ * screenshot was taken.
+ */
+ public void takeScreenshot(final int screenshotType, final boolean hasStatus,
+ final boolean hasNav, int source, @NonNull Handler handler,
+ @Nullable Consumer<Uri> completionConsumer) {
+ ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, hasStatus, hasNav);
+ takeScreenshot(screenshotType, SCREENSHOT_TIMEOUT_MS, handler, screenshotRequest,
+ completionConsumer);
+ }
+
+ /**
+ * Request a screenshot be taken, with provided reason.
+ *
+ * @param screenshotType The type of screenshot, for example either
+ * {@link android.view.WindowManager#TAKE_SCREENSHOT_FULLSCREEN}
+ * or
+ * {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
+ * @param hasStatus {@code true} if the status bar is currently showing. {@code false}
* if
* not.
* @param hasNav {@code true} if the navigation bar is currently showing. {@code
@@ -64,7 +198,7 @@ public class ScreenshotHelper {
}
/**
- * Request a screenshot be taken.
+ * Request a screenshot be taken with a specific timeout.
*
* Added to support reducing unit test duration; the method variant without a timeout argument
* is recommended for general use.
@@ -89,9 +223,9 @@ public class ScreenshotHelper {
public void takeScreenshot(final int screenshotType, final boolean hasStatus,
final boolean hasNav, long timeoutMs, @NonNull Handler handler,
@Nullable Consumer<Uri> completionConsumer) {
- takeScreenshot(screenshotType, hasStatus, hasNav, timeoutMs, handler, null,
- completionConsumer
- );
+ ScreenshotRequest screenshotRequest = new ScreenshotRequest(SCREENSHOT_OTHER, hasStatus,
+ hasNav);
+ takeScreenshot(screenshotType, timeoutMs, handler, screenshotRequest, completionConsumer);
}
/**
@@ -106,23 +240,16 @@ public class ScreenshotHelper {
* screenshot was taken.
*/
public void provideScreenshot(@NonNull Bitmap screenshot, @NonNull Rect boundsInScreen,
- @NonNull Insets insets, int taskId, @NonNull Handler handler,
- @Nullable Consumer<Uri> completionConsumer) {
- Bundle imageBundle = new Bundle();
- imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP, screenshot);
- imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS, boundsInScreen);
- imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_INSETS, insets);
- imageBundle.putInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID, taskId);
-
- takeScreenshot(
- WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE,
- false, false, // ignored when image bundle is set
- SCREENSHOT_TIMEOUT_MS, handler, imageBundle, completionConsumer);
+ @NonNull Insets insets, int taskId, int source,
+ @NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) {
+ ScreenshotRequest screenshotRequest =
+ new ScreenshotRequest(source, screenshot, boundsInScreen, insets, taskId);
+ takeScreenshot(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_TIMEOUT_MS,
+ handler, screenshotRequest, completionConsumer);
}
- private void takeScreenshot(final int screenshotType, final boolean hasStatus,
- final boolean hasNav, long timeoutMs, @NonNull Handler handler,
- @Nullable Bundle providedImage, @Nullable Consumer<Uri> completionConsumer) {
+ private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler,
+ ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
@@ -157,7 +284,7 @@ public class ScreenshotHelper {
return;
}
Messenger messenger = new Messenger(service);
- Message msg = Message.obtain(null, screenshotType);
+ Message msg = Message.obtain(null, screenshotType, screenshotRequest);
final ServiceConnection myConn = this;
Handler h = new Handler(handler.getLooper()) {
@Override
@@ -175,12 +302,6 @@ public class ScreenshotHelper {
}
};
msg.replyTo = new Messenger(h);
- msg.arg1 = hasStatus ? 1 : 0;
- msg.arg2 = hasNav ? 1 : 0;
-
- if (screenshotType == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
- msg.setData(providedImage);
- }
try {
messenger.send(msg);
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index 5424b6f19038..43590bae6770 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -68,6 +68,8 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class IntentForwarderActivityTest {
@@ -633,6 +635,11 @@ public class IntentForwarderActivityTest {
public void onCreate(@Nullable Bundle savedInstanceState) {
getIntent().setComponent(sComponentName);
super.onCreate(savedInstanceState);
+ try {
+ mExecutorService.awaitTermination(/* timeout= */ 30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
@Override
@@ -671,7 +678,8 @@ public class IntentForwarderActivityTest {
}
@Override
- public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
+ public CompletableFuture<ResolveInfo> resolveActivityAsUser(
+ Intent intent, int flags, int userId) {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = sPackageName;
activityInfo.name = sActivityName;
@@ -680,7 +688,7 @@ public class IntentForwarderActivityTest {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = activityInfo;
- return resolveInfo;
+ return CompletableFuture.completedFuture(resolveInfo);
}
@Override
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index cd6b3af5fa6d..fe33cd80f735 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -36,6 +36,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
+import android.view.WindowManager;
import androidx.test.runner.AndroidJUnit4;
@@ -91,7 +92,8 @@ public final class ScreenshotHelperTest {
public void testProvidedImageScreenshot() {
mScreenshotHelper.provideScreenshot(
Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), new Rect(),
- Insets.of(0, 0, 0, 0), 1, mHandler, null);
+ Insets.of(0, 0, 0, 0), 1,
+ WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null);
}
@Test
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index 14249cbe8945..8e8dfaf24b9a 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -78,7 +78,8 @@ class AudioPortEventHandler {
listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
}
} else {
- listeners = mListeners;
+ listeners = (ArrayList<AudioManager.OnAudioPortUpdateListener>)
+ mListeners.clone();
}
}
// reset audio port cache if the event corresponds to a change coming
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 97d32d88c7f2..c652628eb425 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -388,7 +388,7 @@ public final class MediaCas implements AutoCloseable {
@Override
public void onReclaimResources() {
synchronized (mSessionMap) {
- mSessionMap.forEach((casSession, sessionResourceId) -> casSession.close());
+ mSessionMap.forEach((casSession, sessionResourceHandle) -> casSession.close());
}
mEventHandler.sendMessage(mEventHandler.obtainMessage(
EventHandler.MSG_CAS_RESOURCE_LOST));
@@ -868,7 +868,7 @@ public final class MediaCas implements AutoCloseable {
}
}
- private int getSessionResourceId() throws MediaCasException {
+ private int getSessionResourceHandle() throws MediaCasException {
validateInternalStates();
int[] sessionResourceHandle = new int[1];
@@ -881,14 +881,14 @@ public final class MediaCas implements AutoCloseable {
"insufficient resource to Open Session");
}
}
- return sessionResourceHandle[0];
+ return sessionResourceHandle[0];
}
- private void addSessionToResourceMap(Session session, int sessionResourceId) {
+ private void addSessionToResourceMap(Session session, int sessionResourceHandle) {
- if (sessionResourceId != -1) {
+ if (sessionResourceHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
synchronized (mSessionMap) {
- mSessionMap.put(session, sessionResourceId);
+ mSessionMap.put(session, sessionResourceHandle);
}
}
}
@@ -918,13 +918,13 @@ public final class MediaCas implements AutoCloseable {
* @throws MediaCasStateException for CAS-specific state exceptions.
*/
public Session openSession() throws MediaCasException {
- int sessionResourceId = getSessionResourceId();
+ int sessionResourceHandle = getSessionResourceHandle();
try {
OpenSessionCallback cb = new OpenSessionCallback();
mICas.openSession(cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
- addSessionToResourceMap(cb.mSession, sessionResourceId);
+ addSessionToResourceMap(cb.mSession, sessionResourceHandle);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
@@ -952,7 +952,7 @@ public final class MediaCas implements AutoCloseable {
@Nullable
public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode)
throws MediaCasException {
- int sessionResourceId = getSessionResourceId();
+ int sessionResourceHandle = getSessionResourceHandle();
if (mICasV12 == null) {
Log.d(TAG, "Open Session with scrambling mode is only supported by cas@1.2+ interface");
@@ -963,7 +963,7 @@ public final class MediaCas implements AutoCloseable {
OpenSession_1_2_Callback cb = new OpenSession_1_2_Callback();
mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
- addSessionToResourceMap(cb.mSession, sessionResourceId);
+ addSessionToResourceMap(cb.mSession, sessionResourceHandle);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
index adf4d3dcfa09..022cfeeb4e43 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
@@ -80,7 +80,8 @@ public class AudioVolumeGroupChangeHandler {
(AudioManager.VolumeGroupCallback) msg.obj);
}
} else {
- listeners = mListeners;
+ listeners = (ArrayList<AudioManager.VolumeGroupCallback>)
+ mListeners.clone();
}
}
if (listeners.isEmpty()) {
diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 7077cd1b76a0..487b444eb627 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -218,11 +218,11 @@ interface ITunerResourceManager {
* <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this request.
*
* @param request {@link CasSessionRequest} information of the current request.
- * @param sessionResourceId a one-element array to return the granted cas session id.
+ * @param casSessionHandle a one-element array to return the granted cas session handle.
*
* @return true if there is CAS session granted.
*/
- boolean requestCasSession(in CasSessionRequest request, out int[] sessionResourceId);
+ boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle);
/*
* This API is used by the Tuner framework to request an available Lnb from the TunerHAL.
@@ -276,7 +276,7 @@ interface ITunerResourceManager {
*
* <p>Client must call this whenever it releases a descrambler.
*
- * @param demuxHandle the handle of the released Tuner Descrambler.
+ * @param descramblerHandle the handle of the released Tuner Descrambler.
* @param clientId the id of the client that is releasing the descrambler.
*/
void releaseDescrambler(in int descramblerHandle, int clientId);
@@ -288,10 +288,10 @@ interface ITunerResourceManager {
*
* <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this release.
*
- * @param sessionResourceId the id of the released CAS session.
+ * @param casSessionHandle the handle of the released CAS session.
* @param clientId the id of the client that is releasing the cas session.
*/
- void releaseCasSession(in int sessionResourceId, int clientId);
+ void releaseCasSession(in int casSessionHandle, int clientId);
/*
* Notifies the TRM that the Lnb with the given handle was released.
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index b4dcc5d8b3df..be102d8acc10 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -362,17 +362,16 @@ public class TunerResourceManager {
* request.
*
* @param request {@link CasSessionRequest} information of the current request.
- * @param sessionResourceId a one-element array to return the granted cas session id.
- * If no CAS granted, this will return
- * {@link #INVALID_CAS_SESSION_RESOURCE_ID}.
+ * @param casSessionHandle a one-element array to return the granted cas session handel.
+ * If no CAS granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
*
* @return true if there is CAS session granted.
*/
public boolean requestCasSession(@NonNull CasSessionRequest request,
- @NonNull int[] sessionResourceId) {
+ @NonNull int[] casSessionHandle) {
boolean result = false;
try {
- result = mService.requestCasSession(request, sessionResourceId);
+ result = mService.requestCasSession(request, casSessionHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -471,12 +470,12 @@ public class TunerResourceManager {
* <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this
* release.
*
- * @param sessionResourceId the id of the released CAS session.
+ * @param casSessionHandle the handle of the released CAS session.
* @param clientId the id of the client that is releasing the cas session.
*/
- public void releaseCasSession(int sessionResourceId, int clientId) {
+ public void releaseCasSession(int casSessionHandle, int clientId) {
try {
- mService.releaseCasSession(sessionResourceId, clientId);
+ mService.releaseCasSession(casSessionHandle, clientId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
index 7004fb64ba06..8b235e6f246e 100644
--- a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -29,8 +29,6 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- androidprv:layout_maxWidth="@dimen/keyguard_security_width"
- androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:gravity="center">
<include layout="@layout/keyguard_message_area" />
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index e6fb501d02e1..a5445c56112e 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -86,25 +86,25 @@
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
- <item>com.android.systemui.recents.Recents</item>
+<!-- <item>com.android.systemui.recents.Recents</item>-->
<item>com.android.systemui.volume.VolumeUI</item>
- <item>com.android.systemui.stackdivider.Divider</item>
+<!-- <item>com.android.systemui.stackdivider.Divider</item>-->
<!-- <item>com.android.systemui.statusbar.phone.StatusBar</item>-->
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
- <item>com.android.systemui.keyboard.KeyboardUI</item>
- <item>com.android.systemui.pip.PipUI</item>
- <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
+<!-- <item>com.android.systemui.keyboard.KeyboardUI</item>-->
+<!-- <item>com.android.systemui.pip.PipUI</item>-->
+<!-- <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>-->
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
- <item>com.android.systemui.LatencyTester</item>
- <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
+<!-- <item>com.android.systemui.LatencyTester</item>-->
+<!-- <item>com.android.systemui.globalactions.GlobalActionsComponent</item>-->
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.biometrics.AuthController</item>
- <item>com.android.systemui.SliceBroadcastRelayHandler</item>
+<!-- <item>com.android.systemui.SliceBroadcastRelayHandler</item>-->
<item>com.android.systemui.SizeCompatModeActivityController</item>
- <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
+<!-- <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>-->
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.navigationbar.car.CarNavigationBar</item>
<item>com.android.systemui.toast.ToastUI</item>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 60ee19eec25f..4fde30987e50 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -205,6 +205,9 @@ public class CarKeyguardViewController extends OverlayViewController implements
@Override
public void onCancelClicked() {
+ getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false);
+ getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false);
+
mBouncer.hide(/* destroyView= */ true);
mKeyguardCancelClickedListener.onCancelClicked();
}
@@ -226,7 +229,8 @@ public class CarKeyguardViewController extends OverlayViewController implements
@Override
public void setNeedsInput(boolean needsInput) {
- getLayout().setFocusable(needsInput);
+ getOverlayViewGlobalStateController().setWindowFocusable(needsInput);
+ getOverlayViewGlobalStateController().setWindowNeedsInput(needsInput);
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 9fdfc0fff307..2c2aec21ea4f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -52,7 +52,6 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
@@ -80,7 +79,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
private final Handler mMainHandler;
private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
- private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private final ButtonSelectionStateController mButtonSelectionStateController;
private final PhoneStatusBarPolicy mIconPolicy;
private final StatusBarIconController mIconController;
@@ -127,7 +125,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
@Main Handler mainHandler,
Lazy<KeyguardStateController> keyguardStateControllerLazy,
Lazy<NavigationBarController> navigationBarControllerLazy,
- SuperStatusBarViewFactory superStatusBarViewFactory,
ButtonSelectionStateController buttonSelectionStateController,
PhoneStatusBarPolicy iconPolicy,
StatusBarIconController iconController
@@ -143,7 +140,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
mMainHandler = mainHandler;
mKeyguardStateControllerLazy = keyguardStateControllerLazy;
mNavigationBarControllerLazy = navigationBarControllerLazy;
- mSuperStatusBarViewFactory = superStatusBarViewFactory;
mButtonSelectionStateController = buttonSelectionStateController;
mIconPolicy = iconPolicy;
mIconController = iconController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
index 402d742cb949..5fe03f17919b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
@@ -123,6 +123,12 @@ public class OverlayViewGlobalStateController {
mSystemUIOverlayWindowController.setWindowFocusable(focusable);
}
+ /** Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
+ * sysui overlay window */
+ public void setWindowNeedsInput(boolean needsInput) {
+ mSystemUIOverlayWindowController.setWindowNeedsInput(needsInput);
+ }
+
/** Returns {@code true} if the window is focusable. */
public boolean isWindowFocusable() {
return mSystemUIOverlayWindowController.isWindowFocusable();
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
index 04d69eabaeac..5df5d6e98f18 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
@@ -132,6 +132,16 @@ public class SystemUIOverlayWindowController implements
updateWindow();
}
+ /** Sets the window to enable IME. */
+ public void setWindowNeedsInput(boolean needsInput) {
+ if (needsInput) {
+ mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ } else {
+ mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ }
+ updateWindow();
+ }
+
/** Returns {@code true} if the window is visible */
public boolean isWindowVisible() {
return mVisible;
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
index 3ecb29f95092..6da34d4dddc0 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
@@ -38,7 +38,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -75,8 +74,6 @@ public class CarNavigationBarTest extends SysuiTestCase {
@Mock
private NavigationBarController mNavigationBarController;
@Mock
- private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
- @Mock
private ButtonSelectionStateController mButtonSelectionStateController;
@Mock
private PhoneStatusBarPolicy mIconPolicy;
@@ -92,8 +89,7 @@ public class CarNavigationBarTest extends SysuiTestCase {
mCarNavigationBarController, mWindowManager, mDeviceProvisionedController,
new CommandQueue(mContext), mAutoHideController, mButtonSelectionStateListener,
mHandler, () -> mKeyguardStateController, () -> mNavigationBarController,
- mSuperStatusBarViewFactory, mButtonSelectionStateController, mIconPolicy,
- mIconController);
+ mButtonSelectionStateController, mIconPolicy, mIconController);
}
@Test
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index 653c8addba98..deccde593315 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -33,6 +33,7 @@ import android.util.Log;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -395,8 +396,12 @@ public class EnableZenModeDialog {
button1.setAlpha(button1.isEnabled() ? 1f : .5f);
button2.setAlpha(button2.isEnabled() ? 1f : .5f);
} else {
- button1.setVisibility(View.GONE);
- button2.setVisibility(View.GONE);
+ if (button1 != null) {
+ ((ViewGroup) row).removeView(button1);
+ }
+ if (button2 != null) {
+ ((ViewGroup) row).removeView(button2);
+ }
}
}
diff --git a/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml b/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
new file mode 100644
index 000000000000..a793680a037d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml
@@ -0,0 +1,21 @@
+<?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">
+ <item android:state_pressed="true">
+ <ripple android:color="#99999999" />
+ </item>
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubble_manage_menu.xml b/packages/SystemUI/res/layout/bubble_manage_menu.xml
new file mode 100644
index 000000000000..129282dae77f
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_manage_menu.xml
@@ -0,0 +1,99 @@
+<?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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/rounded_bg_full"
+ android:elevation="@dimen/bubble_manage_menu_elevation"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/bubble_manage_menu_dismiss_container"
+ android:background="@drawable/bubble_manage_menu_row"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:gravity="center_vertical"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_remove_no_shadow"
+ android:tint="@color/global_actions_text"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
+ android:text="@string/bubble_dismiss_text" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/bubble_manage_menu_dont_bubble_container"
+ android:background="@drawable/bubble_manage_menu_row"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:gravity="center_vertical"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_stop_bubble"
+ android:tint="@color/global_actions_text"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault"
+ android:text="@string/bubbles_dont_bubble_conversation" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/bubble_manage_menu_settings_container"
+ android:background="@drawable/bubble_manage_menu_row"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:gravity="center_vertical"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/bubble_manage_menu_settings_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:src="@drawable/ic_remove_no_shadow"/>
+
+ <TextView
+ android:id="@+id/bubble_manage_menu_settings_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7cefa0c498d5..179f8b8ea9f4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1200,6 +1200,7 @@
snap to the dismiss target. -->
<dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
<dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
+ <dimen name="bubble_manage_menu_elevation">4dp</dimen>
<dimen name="dismiss_circle_size">52dp</dimen>
<dimen name="dismiss_target_x_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cb20e7a15424..d639ed074240 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2629,6 +2629,8 @@
<string name="bubbles_user_education_manage">Tap Manage to turn off bubbles from this app</string>
<!-- Button text for dismissing the bubble "manage" button tool tip [CHAR LIMIT=20]-->
<string name="bubbles_user_education_got_it">Got it</string>
+ <!-- Label for the button that takes the user to the notification settings for the given app. -->
+ <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
<!-- Notification content text when the system navigation mode changes as a result of changing the default launcher [CHAR LIMIT=NONE] -->
<string name="notification_content_system_nav_changed">System navigation updated. To make changes, go to Settings.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 7262f8caac89..1f27ae238533 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
+
import android.accessibilityservice.AccessibilityService;
import android.app.PendingIntent;
import android.app.RemoteAction;
@@ -282,8 +284,8 @@ public class SystemActions extends SystemUI {
private void handleTakeScreenshot() {
ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext);
- screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
- true, true, new Handler(Looper.getMainLooper()), null);
+ screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, true, true,
+ SCREENSHOT_GLOBAL_ACTIONS, new Handler(Looper.getMainLooper()), null);
}
private void handleAccessibilityMenu() {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 71f2bc09b983..38bfffbb75d9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -90,6 +90,7 @@ class Bubble implements BubbleViewProvider {
}
private FlyoutMessage mFlyoutMessage;
+ private Drawable mBadgedAppIcon;
private Bitmap mBadgedImage;
private int mDotColor;
private Path mDotPath;
@@ -133,6 +134,10 @@ class Bubble implements BubbleViewProvider {
return mBadgedImage;
}
+ public Drawable getBadgedAppIcon() {
+ return mBadgedAppIcon;
+ }
+
@Override
public int getDotColor() {
return mDotColor;
@@ -239,6 +244,7 @@ class Bubble implements BubbleViewProvider {
mAppName = info.appName;
mFlyoutMessage = info.flyoutMessage;
+ mBadgedAppIcon = info.badgedAppIcon;
mBadgedImage = info.badgedBubbleImage;
mDotColor = info.dotColor;
mDotPath = info.dotPath;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c6883c89bd21..e488cf271fdf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -605,6 +605,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (mExpandListener != null) {
mStackView.setExpandListener(mExpandListener);
}
+
+ mStackView.setUnbubbleConversationCallback(notificationEntry ->
+ onUserChangedBubble(notificationEntry, false /* shouldBubble */));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 3524696dbc79..bb2365559f74 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -43,7 +43,6 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -55,14 +54,13 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
-import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.AlphaOptimizedButton;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Container for the expanded bubble view, handles rendering the caret and settings icon.
*/
-public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
+public class BubbleExpandedView extends LinearLayout {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
private enum ActivityViewStatus {
@@ -100,9 +98,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private int mPointerWidth;
private int mPointerHeight;
private ShapeDrawable mPointerDrawable;
- private Rect mTempRect = new Rect();
- private int[] mTempLoc = new int[2];
- private int mExpandedViewTouchSlop;
@Nullable private Bubble mBubble;
@@ -224,7 +219,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
- mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
}
@Override
@@ -239,7 +233,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
-
mPointerDrawable = new ShapeDrawable(TriangleShape.create(
mPointerWidth, mPointerHeight, true /* pointUp */));
mPointerView.setBackground(mPointerDrawable);
@@ -248,7 +241,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
R.dimen.bubble_manage_button_height);
mSettingsIcon = findViewById(R.id.settings_button);
- mSettingsIcon.setOnClickListener(this);
mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
true /* singleTaskInstance */);
@@ -289,6 +281,19 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
return mBubble != null ? mBubble.getEntry() : null;
}
+ void setManageClickListener(OnClickListener manageClickListener) {
+ findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
+ }
+
+ /**
+ * Updates the ActivityView's obscured touchable region. This calls onLocationChanged, which
+ * results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is useful
+ * if a view has been added or removed from on top of the ActivityView, such as the manage menu.
+ */
+ void updateObscuredTouchableRegion() {
+ mActivityView.onLocationChanged();
+ }
+
void applyThemeAttrs() {
final TypedArray ta = mContext.obtainStyledAttributes(
new int[] {
@@ -473,51 +478,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
/**
- * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
- * expanded view.
- *
- * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
- */
- boolean intersectingTouchableContent(int rawX, int rawY) {
- mTempRect.setEmpty();
- if (mActivityView != null) {
- mTempLoc = mActivityView.getLocationOnScreen();
- mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
- mTempLoc[1] - mExpandedViewTouchSlop,
- mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
- mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
- }
- if (mTempRect.contains(rawX, rawY)) {
- return true;
- }
- mTempLoc = mSettingsIcon.getLocationOnScreen();
- mTempRect.set(mTempLoc[0],
- mTempLoc[1],
- mTempLoc[0] + mSettingsIcon.getWidth(),
- mTempLoc[1] + mSettingsIcon.getHeight());
- if (mTempRect.contains(rawX, rawY)) {
- return true;
- }
- return false;
- }
-
- @Override
- public void onClick(View view) {
- if (mBubble == null) {
- return;
- }
- int id = view.getId();
- if (id == R.id.settings_button) {
- Intent intent = mBubble.getSettingsIntent();
- mStackView.collapseStack(() -> {
- mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser());
- logBubbleClickEvent(mBubble,
- SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
- });
- }
- }
-
- /**
* Update appearance of the expanded view being displayed.
*/
public void updateView() {
@@ -547,10 +507,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
* Position of the manage button displayed in the expanded view. Used for placing user
* education about the manage button.
*/
- public Rect getManageButtonLocationOnScreen() {
- mTempLoc = mSettingsIcon.getLocationOnScreen();
- return new Rect(mTempLoc[0], mTempLoc[1], mTempLoc[0] + mSettingsIcon.getWidth(),
- mTempLoc[1] + mSettingsIcon.getHeight());
+ public void getManageButtonBoundsOnScreen(Rect rect) {
+ mSettingsIcon.getBoundsOnScreen(rect);
}
/**
@@ -611,26 +569,4 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
return INVALID_DISPLAY;
}
-
- /**
- * Logs bubble UI click event.
- *
- * @param bubble the bubble notification entry that user is interacting with.
- * @param action the user interaction enum.
- */
- private void logBubbleClickEvent(Bubble bubble, int action) {
- StatusBarNotification notification = bubble.getEntry().getSbn();
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- notification.getPackageName(),
- notification.getNotification().getChannelId(),
- notification.getId(),
- mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
- mStackView.getBubbleCount(),
- action,
- mStackView.getNormalizedXPosition(),
- mStackView.getNormalizedYPosition(),
- bubble.showInShade(),
- bubble.isOngoing(),
- false /* isAppForeground (unused) */);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0aabdff96e1e..c9069316028c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -33,12 +33,14 @@ import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
@@ -47,6 +49,7 @@ import android.graphics.RectF;
import android.graphics.Region;
import android.os.Bundle;
import android.os.Vibrator;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.Choreographer;
import android.view.DisplayCutout;
@@ -55,6 +58,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -62,6 +66,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.MainThread;
@@ -83,6 +88,7 @@ import com.android.systemui.bubbles.animation.StackAnimationController;
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -97,6 +103,7 @@ import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.function.Consumer;
/**
* Renders bubbles in a stack and handles animating expanded and collapsed states.
@@ -224,7 +231,7 @@ public class BubbleStackView extends FrameLayout {
private int mPointerHeight;
private int mStatusBarHeight;
private int mImeOffset;
- private BubbleViewProvider mExpandedBubble;
+ @Nullable private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded;
/** Whether the stack is currently on the left side of the screen, or animating there. */
@@ -244,6 +251,10 @@ public class BubbleStackView extends FrameLayout {
}
private BubbleController.BubbleExpandListener mExpandListener;
+
+ /** Callback to run when we want to unbubble the given notification's conversation. */
+ private Consumer<NotificationEntry> mUnbubbleConversationCallback;
+
private SysUiState mSysUiState;
private boolean mViewUpdatedRequested = false;
@@ -255,9 +266,7 @@ public class BubbleStackView extends FrameLayout {
private LayoutInflater mInflater;
- // Used for determining view / touch intersection
- int[] mTempLoc = new int[2];
- RectF mTempRect = new RectF();
+ private Rect mTempRect = new Rect();
private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
@@ -471,6 +480,11 @@ public class BubbleStackView extends FrameLayout {
return true;
}
+ // If the manage menu is visible, just hide it.
+ if (mShowingManage) {
+ showManageMenu(false /* show */);
+ }
+
if (mBubbleData.isExpanded()) {
maybeShowManageEducation(false /* show */);
@@ -627,6 +641,13 @@ public class BubbleStackView extends FrameLayout {
private BubbleManageEducationView mManageEducationView;
private boolean mAnimatingManageEducationAway;
+ private ViewGroup mManageMenu;
+ private ImageView mManageSettingsIcon;
+ private TextView mManageSettingsText;
+ private boolean mShowingManage = false;
+ private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
+ SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+
@SuppressLint("ClickableViewAccessibility")
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
@@ -689,6 +710,8 @@ public class BubbleStackView extends FrameLayout {
mExpandedViewContainer.setClipChildren(false);
addView(mExpandedViewContainer);
+ setUpManageMenu();
+
setUpFlyout();
mFlyoutTransitionSpring.setSpring(new SpringForce()
.setStiffness(SpringForce.STIFFNESS_LOW)
@@ -838,7 +861,9 @@ public class BubbleStackView extends FrameLayout {
// ActivityViews, etc.) were touched. Collapse the stack if it's expanded.
setOnTouchListener((view, ev) -> {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if (mBubbleData.isExpanded()) {
+ if (mShowingManage) {
+ showManageMenu(false /* show */);
+ } else if (mBubbleData.isExpanded()) {
mBubbleData.setExpanded(false);
}
}
@@ -847,6 +872,66 @@ public class BubbleStackView extends FrameLayout {
});
}
+ private void setUpManageMenu() {
+ if (mManageMenu != null) {
+ removeView(mManageMenu);
+ }
+
+ mManageMenu = (ViewGroup) LayoutInflater.from(getContext()).inflate(
+ R.layout.bubble_manage_menu, this, false);
+ mManageMenu.setVisibility(View.INVISIBLE);
+
+ PhysicsAnimator.getInstance(mManageMenu).setDefaultSpringConfig(mManageSpringConfig);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {android.R.attr.dialogCornerRadius});
+ final int menuCornerRadius = ta.getDimensionPixelSize(0, 0);
+ ta.recycle();
+
+ mManageMenu.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), menuCornerRadius);
+ }
+ });
+ mManageMenu.setClipToOutline(true);
+
+ mManageMenu.findViewById(R.id.bubble_manage_menu_dismiss_container).setOnClickListener(
+ view -> {
+ showManageMenu(false /* show */);
+ dismissBubbleIfExists(mBubbleData.getSelectedBubble());
+ });
+
+ mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener(
+ view -> {
+ showManageMenu(false /* show */);
+ final Bubble bubble = mBubbleData.getSelectedBubble();
+ if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
+ mUnbubbleConversationCallback.accept(bubble.getEntry());
+ }
+ });
+
+ mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener(
+ view -> {
+ showManageMenu(false /* show */);
+ final Bubble bubble = mBubbleData.getSelectedBubble();
+ if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
+ final Intent intent = bubble.getSettingsIntent();
+ collapseStack(() -> {
+ mContext.startActivityAsUser(
+ intent, bubble.getEntry().getSbn().getUser());
+ logBubbleClickEvent(
+ bubble,
+ SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
+ });
+ }
+ });
+
+ mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon);
+ mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name);
+ addView(mManageMenu);
+ }
+
private void setUpUserEducation() {
if (mUserEducationView != null) {
removeView(mUserEducationView);
@@ -934,6 +1019,7 @@ public class BubbleStackView extends FrameLayout {
setUpFlyout();
setUpOverflow();
setUpUserEducation();
+ setUpManageMenu();
}
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -960,6 +1046,9 @@ public class BubbleStackView extends FrameLayout {
Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation));
addOnLayoutChangeListener(mOrientationChangedListener);
hideFlyoutImmediate();
+
+ mManageMenu.setVisibility(View.INVISIBLE);
+ mShowingManage = false;
}
@Override
@@ -1100,6 +1189,12 @@ public class BubbleStackView extends FrameLayout {
mExpandListener = listener;
}
+ /** Sets the function to call to un-bubble the given conversation. */
+ public void setUnbubbleConversationCallback(
+ Consumer<NotificationEntry> unbubbleConversationCallback) {
+ mUnbubbleConversationCallback = unbubbleConversationCallback;
+ }
+
/**
* Whether the stack of bubbles is expanded or not.
*/
@@ -1361,15 +1456,14 @@ public class BubbleStackView extends FrameLayout {
mManageEducationView.setAlpha(0);
mManageEducationView.setVisibility(VISIBLE);
mManageEducationView.post(() -> {
- final Rect position =
- mExpandedBubble.getExpandedView().getManageButtonLocationOnScreen();
+ mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
final int viewHeight = mManageEducationView.getManageViewHeight();
final int inset = getResources().getDimensionPixelSize(
R.dimen.bubbles_manage_education_top_inset);
mManageEducationView.bringToFront();
- mManageEducationView.setManageViewPosition(position.left,
- position.top - viewHeight + inset);
- mManageEducationView.setPointerPosition(position.centerX() - position.left);
+ mManageEducationView.setManageViewPosition(mTempRect.left,
+ mTempRect.top - viewHeight + inset);
+ mManageEducationView.setPointerPosition(mTempRect.centerX() - mTempRect.left);
mManageEducationView.animate()
.setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
.setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
@@ -1443,6 +1537,9 @@ public class BubbleStackView extends FrameLayout {
}
private void animateCollapse() {
+ // Hide the menu if it's visible.
+ showManageMenu(false);
+
mIsExpanded = false;
final BubbleViewProvider previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
@@ -1570,9 +1667,9 @@ public class BubbleStackView extends FrameLayout {
*/
@Override
public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
- // If the notification shade is expanded, we shouldn't let the ActivityView steal any touch
- // events from any location.
- if (mNotificationShadeWindowController.getPanelExpanded()) {
+ // If the notification shade is expanded, or the manage menu is open, we shouldn't let the
+ // ActivityView steal any touch events from any location.
+ if (mNotificationShadeWindowController.getPanelExpanded() || mShowingManage) {
touchableRegion.setEmpty();
}
}
@@ -1658,17 +1755,20 @@ public class BubbleStackView extends FrameLayout {
private void dismissMagnetizedObject() {
if (mIsExpanded) {
final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
- final Bubble draggedOutBubble = mBubbleData.getBubbleWithView(draggedOutBubbleView);
+ dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView));
- if (mBubbleData.hasBubbleWithKey(draggedOutBubble.getKey())) {
- mBubbleData.notificationEntryRemoved(
- draggedOutBubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
- }
} else {
mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
}
}
+ private void dismissBubbleIfExists(@Nullable Bubble bubble) {
+ if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) {
+ mBubbleData.notificationEntryRemoved(
+ bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ }
+ }
+
/** Prepares and starts the desaturate/darken animation on the bubble stack. */
private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
mDesaturateAndDarkenTargetView = targetView;
@@ -1912,6 +2012,63 @@ public class BubbleStackView extends FrameLayout {
invalidate();
}
+ private void showManageMenu(boolean show) {
+ mShowingManage = show;
+
+ // This should not happen, since the manage menu is only visible when there's an expanded
+ // bubble. If we end up in this state, just hide the menu immediately.
+ if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
+ mManageMenu.setVisibility(View.INVISIBLE);
+ return;
+ }
+
+ // If available, update the manage menu's settings option with the expanded bubble's app
+ // name and icon.
+ if (show && mBubbleData.hasBubbleWithKey(mExpandedBubble.getKey())) {
+ final Bubble bubble = mBubbleData.getBubbleWithKey(mExpandedBubble.getKey());
+ mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon());
+ mManageSettingsText.setText(getResources().getString(
+ R.string.bubbles_app_settings, bubble.getAppName()));
+ }
+
+ mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
+
+ // When the menu is open, it should be at these coordinates. This will make the menu's
+ // bottom left corner match up with the button's bottom left corner.
+ final float targetX = mTempRect.left;
+ final float targetY = mTempRect.bottom - mManageMenu.getHeight();
+
+ if (show) {
+ mManageMenu.setScaleX(0.5f);
+ mManageMenu.setScaleY(0.5f);
+ mManageMenu.setTranslationX(targetX - mManageMenu.getWidth() / 4);
+ mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4);
+ mManageMenu.setAlpha(0f);
+
+ PhysicsAnimator.getInstance(mManageMenu)
+ .spring(DynamicAnimation.ALPHA, 1f)
+ .spring(DynamicAnimation.SCALE_X, 1f)
+ .spring(DynamicAnimation.SCALE_Y, 1f)
+ .spring(DynamicAnimation.TRANSLATION_X, targetX)
+ .spring(DynamicAnimation.TRANSLATION_Y, targetY)
+ .start();
+
+ mManageMenu.setVisibility(View.VISIBLE);
+ } else {
+ PhysicsAnimator.getInstance(mManageMenu)
+ .spring(DynamicAnimation.ALPHA, 0f)
+ .spring(DynamicAnimation.SCALE_X, 0.5f)
+ .spring(DynamicAnimation.SCALE_Y, 0.5f)
+ .spring(DynamicAnimation.TRANSLATION_X, targetX - mManageMenu.getWidth() / 4)
+ .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4)
+ .withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE))
+ .start();
+ }
+
+ // Update the AV's obscured touchable region for the new menu visibility state.
+ mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
+ }
+
private void updateExpandedBubble() {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "updateExpandedBubble()");
@@ -1921,6 +2078,7 @@ public class BubbleStackView extends FrameLayout {
&& mExpandedBubble.getExpandedView() != null) {
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
mExpandedViewContainer.addView(bev);
+ bev.setManageClickListener((view) -> showManageMenu(!mShowingManage));
bev.populateExpandedView();
mExpandedViewContainer.setVisibility(VISIBLE);
mExpandedViewContainer.setAlpha(1.0f);
@@ -2089,4 +2247,26 @@ public class BubbleStackView extends FrameLayout {
}
return bubbles;
}
+
+ /**
+ * Logs bubble UI click event.
+ *
+ * @param bubble the bubble notification entry that user is interacting with.
+ * @param action the user interaction enum.
+ */
+ private void logBubbleClickEvent(Bubble bubble, int action) {
+ StatusBarNotification notification = bubble.getEntry().getSbn();
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ notification.getPackageName(),
+ notification.getNotification().getChannelId(),
+ notification.getId(),
+ getBubbleIndex(getExpandedBubble()),
+ getBubbleCount(),
+ action,
+ getNormalizedXPosition(),
+ getNormalizedYPosition(),
+ bubble.showInShade(),
+ bubble.isOngoing(),
+ false /* isAppForeground (unused) */);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index c96f9a470ca4..8a57a735f6cb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -116,6 +116,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
ShortcutInfo shortcutInfo;
String appName;
Bitmap badgedBubbleImage;
+ Drawable badgedAppIcon;
int dotColor;
Path dotPath;
Bubble.FlyoutMessage flyoutMessage;
@@ -176,6 +177,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
}
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon);
+ info.badgedAppIcon = badgedIcon;
info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
badgeBitmapInfo).icon;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index ef84c73b3145..ca3e2e27fa9a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -20,15 +20,17 @@ import android.graphics.Bitmap;
import android.graphics.Path;
import android.view.View;
+import androidx.annotation.Nullable;
+
/**
* Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
*/
interface BubbleViewProvider {
- BubbleExpandedView getExpandedView();
+ @Nullable BubbleExpandedView getExpandedView();
void setContentVisibility(boolean visible);
- View getIconView();
+ @Nullable View getIconView();
void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b88350f52678..b211ccc28803 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -16,6 +16,8 @@ package com.android.systemui.globalactions;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
+import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
@@ -480,7 +482,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
*/
@VisibleForTesting
protected int getMaxShownPowerItems() {
- if (shouldUseControlsLayout()) {
+ // TODO: Overflow disabled on keyguard while we solve for touch blocking issues.
+ if (shouldUseControlsLayout() && !mKeyguardShowing) {
return mResources.getInteger(com.android.systemui.R.integer.power_menu_max_columns);
} else {
return Integer.MAX_VALUE;
@@ -830,7 +833,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
- mScreenshotHelper.takeScreenshot(1, true, true, mHandler, null);
+ mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, true, true,
+ SCREENSHOT_GLOBAL_ACTIONS, mHandler, null);
mMetricsLogger.action(MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_PRESS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 66bc177da81d..fecb7b602012 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
@@ -380,7 +381,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
Insets visibleInsets, int taskId) {
mScreenshotHelper.provideScreenshot(screenImage, locationInScreen, visibleInsets,
- taskId, mHandler, null);
+ taskId, SCREENSHOT_OVERVIEW, mHandler, null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index e1cc0b0c90c2..1efe663ca6ce 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -76,6 +76,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -104,7 +105,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
*/
static class SaveImageInBackgroundData {
public Bitmap image;
- public Uri imageUri;
public Consumer<Uri> finisher;
public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
public int errorMsgResId;
@@ -112,13 +112,33 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
void clearImage() {
image = null;
- imageUri = null;
+ }
+ }
+
+ /**
+ * Structure returned by the SaveImageInBackgroundTask
+ */
+ static class SavedImageData {
+ public Uri uri;
+ public Notification.Action shareAction;
+ public Notification.Action editAction;
+ public Notification.Action deleteAction;
+ public List<Notification.Action> smartActions;
+
+ /**
+ * Used to reset the return data on error
+ */
+ public void reset() {
+ uri = null;
+ shareAction = null;
+ editAction = null;
+ deleteAction = null;
+ smartActions = null;
}
}
abstract static class ActionsReadyListener {
- abstract void onActionsReady(Uri imageUri, List<Notification.Action> smartActions,
- List<Notification.Action> actions);
+ abstract void onActionsReady(SavedImageData imageData);
}
// These strings are used for communicating the action invoked to
@@ -147,6 +167,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
private static final int MESSAGE_CORNER_TIMEOUT = 2;
private final ScreenshotNotificationsController mNotificationsController;
+ private final UiEventLogger mUiEventLogger;
private final Context mContext;
private final WindowManager mWindowManager;
@@ -185,6 +206,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_CORNER_TIMEOUT:
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
GlobalScreenshot.this.clearScreenshot("timeout");
break;
default:
@@ -199,9 +221,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
@Inject
public GlobalScreenshot(
Context context, @Main Resources resources, LayoutInflater layoutInflater,
- ScreenshotNotificationsController screenshotNotificationsController) {
+ ScreenshotNotificationsController screenshotNotificationsController,
+ UiEventLogger uiEventLogger) {
mContext = context;
mNotificationsController = screenshotNotificationsController;
+ mUiEventLogger = uiEventLogger;
// Inflate the screenshot layout
mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
@@ -222,7 +246,10 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
mBackgroundProtection = mScreenshotLayout.findViewById(
R.id.global_screenshot_actions_background);
mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
- mDismissButton.setOnClickListener(view -> clearScreenshot("dismiss_button"));
+ mDismissButton.setOnClickListener(view -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
+ clearScreenshot("dismiss_button");
+ });
mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
@@ -445,12 +472,14 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
@Override
- void onActionsReady(Uri uri, List<Notification.Action> smartActions,
- List<Notification.Action> actions) {
- if (uri == null) {
+ void onActionsReady(SavedImageData imageData) {
+ finisher.accept(imageData.uri);
+ if (imageData.uri == null) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
} else {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
mScreenshotHandler.post(() -> {
if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
mScreenshotAnimation.addListener(
@@ -458,13 +487,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- createScreenshotActionsShadeAnimation(
- smartActions, actions).start();
+ createScreenshotActionsShadeAnimation(imageData)
+ .start();
}
});
} else {
- createScreenshotActionsShadeAnimation(smartActions,
- actions).start();
+ createScreenshotActionsShadeAnimation(imageData).start();
}
});
}
@@ -567,8 +595,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
return dropInAnimation;
}
- private ValueAnimator createScreenshotActionsShadeAnimation(
- List<Notification.Action> smartActions, List<Notification.Action> actions) {
+ private ValueAnimator createScreenshotActionsShadeAnimation(SavedImageData imageData) {
LayoutInflater inflater = LayoutInflater.from(mContext);
mActionsView.removeAllViews();
mActionsContainer.setScrollX(0);
@@ -583,45 +610,63 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
} catch (RemoteException e) {
}
- for (Notification.Action smartAction : smartActions) {
+ for (Notification.Action smartAction : imageData.smartActions) {
ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
actionChip.setText(smartAction.title);
actionChip.setIcon(smartAction.getIcon(), false);
actionChip.setPendingIntent(smartAction.actionIntent,
- () -> clearScreenshot("chip tapped"));
+ () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
+ clearScreenshot("chip tapped");
+ });
mActionsView.addView(actionChip);
}
- for (Notification.Action action : actions) {
- ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
- R.layout.global_screenshot_action_chip, mActionsView, false);
- actionChip.setText(action.title);
- actionChip.setIcon(action.getIcon(), true);
- actionChip.setPendingIntent(action.actionIntent, () -> clearScreenshot("chip tapped"));
- if (action.actionIntent.getIntent().getAction().equals(Intent.ACTION_EDIT)) {
- mScreenshotView.setOnClickListener(v -> {
- try {
- action.actionIntent.send();
- clearScreenshot("screenshot preview tapped");
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Intent cancelled", e);
- }
- });
- mScreenshotView.setContentDescription(action.title);
+ ScreenshotActionChip shareChip = (ScreenshotActionChip) inflater.inflate(
+ R.layout.global_screenshot_action_chip, mActionsView, false);
+ shareChip.setText(imageData.shareAction.title);
+ shareChip.setIcon(imageData.shareAction.getIcon(), true);
+ shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
+ clearScreenshot("chip tapped");
+ });
+ mActionsView.addView(shareChip);
+
+ ScreenshotActionChip editChip = (ScreenshotActionChip) inflater.inflate(
+ R.layout.global_screenshot_action_chip, mActionsView, false);
+ editChip.setText(imageData.editAction.title);
+ editChip.setIcon(imageData.editAction.getIcon(), true);
+ editChip.setPendingIntent(imageData.editAction.actionIntent, () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
+ clearScreenshot("chip tapped");
+ });
+ mActionsView.addView(editChip);
+
+ mScreenshotView.setOnClickListener(v -> {
+ try {
+ imageData.editAction.actionIntent.send();
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
+ clearScreenshot("screenshot preview tapped");
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Intent cancelled", e);
}
- mActionsView.addView(actionChip);
- }
+ });
+ mScreenshotView.setContentDescription(imageData.editAction.title);
+
if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_SCROLLING_ENABLED, false)) {
ScreenshotActionChip scrollChip = (ScreenshotActionChip) inflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
Toast scrollNotImplemented = Toast.makeText(
mContext, "Not implemented", Toast.LENGTH_SHORT);
- scrollChip.setText("Extend"); // TODO (mkephart): add resource and translate
+ scrollChip.setText("Extend"); // TODO : add resource and translate
scrollChip.setIcon(
Icon.createWithResource(mContext, R.drawable.ic_arrow_downward), true);
- scrollChip.setOnClickListener(v -> scrollNotImplemented.show());
+ scrollChip.setOnClickListener(v -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED);
+ scrollNotImplemented.show();
+ });
mActionsView.addView(scrollChip);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
index f3614ffbdb1b..095c32f4a2ce 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -24,7 +24,6 @@ import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
-import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -53,7 +52,6 @@ import android.widget.Toast;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
-import java.util.List;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -347,14 +345,13 @@ public class GlobalScreenshotLegacy {
// Save the screenshot once we have a bit of time now
saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() {
@Override
- void onActionsReady(Uri uri, List<Notification.Action> smartActions,
- List<Notification.Action> actions) {
- if (uri == null) {
+ void onActionsReady(GlobalScreenshot.SavedImageData actionData) {
+ if (actionData.uri == null) {
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
} else {
mNotificationsController
- .showScreenshotActionsNotification(uri, smartActions, actions);
+ .showScreenshotActionsNotification(actionData);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index c828c4cccce5..170174deaeb3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -83,6 +83,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private final Context mContext;
private final GlobalScreenshot.SaveImageInBackgroundData mParams;
+ private final GlobalScreenshot.SavedImageData mImageData;
private final String mImageFileName;
private final long mImageTime;
private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
@@ -93,6 +94,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
mContext = context;
+ mImageData = new GlobalScreenshot.SavedImageData();
// Prepare all the output metadata
mParams = data;
@@ -145,6 +147,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
values.put(MediaColumns.IS_PENDING, 1);
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+
try {
// First, write the actual data for our screenshot
try (OutputStream out = resolver.openOutputStream(uri)) {
@@ -192,8 +195,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
throw e;
}
- List<Notification.Action> actions =
- populateNotificationActions(mContext, r, uri);
List<Notification.Action> smartActions = new ArrayList<>();
if (mSmartActionsEnabled) {
int timeoutMs = DeviceConfig.getInt(
@@ -206,8 +207,14 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
mSmartActionsProvider),
mContext));
}
- mParams.mActionsReadyListener.onActionsReady(uri, smartActions, actions);
- mParams.imageUri = uri;
+
+ mImageData.uri = uri;
+ mImageData.smartActions = smartActions;
+ mImageData.shareAction = createShareAction(mContext, mContext.getResources(), uri);
+ mImageData.editAction = createEditAction(mContext, mContext.getResources(), uri);
+ mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri);
+
+ mParams.mActionsReadyListener.onActionsReady(mImageData);
mParams.image = null;
mParams.errorMsgResId = 0;
} catch (Exception e) {
@@ -216,29 +223,26 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
Slog.e(TAG, "unable to save screenshot", e);
mParams.clearImage();
mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
- mParams.mActionsReadyListener.onActionsReady(null, null, null);
+ mImageData.reset();
+ mParams.mActionsReadyListener.onActionsReady(mImageData);
}
return null;
}
@Override
- protected void onPostExecute(Void params) {
- mParams.finisher.accept(mParams.imageUri);
- }
-
- @Override
protected void onCancelled(Void params) {
// If we are cancelled while the task is running in the background, we may get null
// params. The finisher is expected to always be called back, so just use the baked-in
// params from the ctor in any case.
- mParams.mActionsReadyListener.onActionsReady(null, null, null);
+ mImageData.reset();
+ mParams.mActionsReadyListener.onActionsReady(mImageData);
mParams.finisher.accept(null);
mParams.clearImage();
}
@VisibleForTesting
- List<Notification.Action> populateNotificationActions(Context context, Resources r, Uri uri) {
+ Notification.Action createShareAction(Context context, Resources r, Uri uri) {
// Note: Both the share and edit actions are proxied through ActionProxyReceiver in
// order to do some common work like dismissing the keyguard and sending
// closeSystemWindows
@@ -263,8 +267,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// by setting the (otherwise unused) request code to the current user id.
int requestCode = context.getUserId();
- ArrayList<Notification.Action> actions = new ArrayList<>();
-
PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
@@ -288,7 +290,15 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_share),
r.getString(com.android.internal.R.string.share), shareAction);
- actions.add(shareActionBuilder.build());
+
+ return shareActionBuilder.build();
+ }
+
+ @VisibleForTesting
+ Notification.Action createEditAction(Context context, Resources r, Uri uri) {
+ // Note: Both the share and edit actions are proxied through ActionProxyReceiver in
+ // order to do some common work like dismissing the keyguard and sending
+ // closeSystemWindows
// Create an edit intent, if a specific package is provided as the editor, then
// launch that directly
@@ -302,6 +312,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ // Make sure pending intents for the system user are still unique across users
+ // by setting the (otherwise unused) request code to the current user id.
+ int requestCode = mContext.getUserId();
+
// Create a edit action
PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
@@ -317,24 +331,30 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_edit),
r.getString(com.android.internal.R.string.screenshot_edit), editAction);
- actions.add(editActionBuilder.build());
-
- if (mCreateDeleteAction) {
- // Create a delete action for the notification
- PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
- new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
- .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
- .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
- .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
- mSmartActionsEnabled)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
- Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
- Icon.createWithResource(r, R.drawable.ic_screenshot_delete),
- r.getString(com.android.internal.R.string.delete), deleteAction);
- actions.add(deleteActionBuilder.build());
- }
- return actions;
+
+ return editActionBuilder.build();
+ }
+
+ @VisibleForTesting
+ Notification.Action createDeleteAction(Context context, Resources r, Uri uri) {
+ // Make sure pending intents for the system user are still unique across users
+ // by setting the (otherwise unused) request code to the current user id.
+ int requestCode = mContext.getUserId();
+
+ // Create a delete action for the notification
+ PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
+ new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
+ .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
+ .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
+ .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
+ mSmartActionsEnabled)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
+ Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
+ Icon.createWithResource(r, R.drawable.ic_screenshot_delete),
+ r.getString(com.android.internal.R.string.delete), deleteAction);
+
+ return deleteActionBuilder.build();
}
private int getUserHandleOfForegroundApplication(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
new file mode 100644
index 000000000000..20fa991dcc1f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+public enum ScreenshotEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "screenshot requested from global actions")
+ SCREENSHOT_REQUESTED_GLOBAL_ACTIONS(302),
+ @UiEvent(doc = "screenshot requested from key chord")
+ SCREENSHOT_REQUESTED_KEY_CHORD(303),
+ @UiEvent(doc = "screenshot requested from other key press (e.g. ctrl-s)")
+ SCREENSHOT_REQUESTED_KEY_OTHER(384),
+ @UiEvent(doc = "screenshot requested from overview")
+ SCREENSHOT_REQUESTED_OVERVIEW(304),
+ @UiEvent(doc = "screenshot requested from accessibility actions")
+ SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS(382),
+ @UiEvent(doc = "screenshot requested (other)")
+ SCREENSHOT_REQUESTED_OTHER(305),
+ @UiEvent(doc = "screenshot was saved")
+ SCREENSHOT_SAVED(306),
+ @UiEvent(doc = "screenshot failed to save")
+ SCREENSHOT_NOT_SAVED(336),
+ @UiEvent(doc = "screenshot preview tapped")
+ SCREENSHOT_PREVIEW_TAPPED(307),
+ @UiEvent(doc = "screenshot edit button tapped")
+ SCREENSHOT_EDIT_TAPPED(308),
+ @UiEvent(doc = "screenshot share button tapped")
+ SCREENSHOT_SHARE_TAPPED(309),
+ @UiEvent(doc = "screenshot smart action chip tapped")
+ SCREENSHOT_SMART_ACTION_TAPPED(374),
+ @UiEvent(doc = "screenshot scroll tapped")
+ SCREENSHOT_SCROLL_TAPPED(373),
+ @UiEvent(doc = "screenshot interaction timed out")
+ SCREENSHOT_INTERACTION_TIMEOUT(310),
+ @UiEvent(doc = "screenshot explicitly dismissed")
+ SCREENSHOT_EXPLICIT_DISMISSAL(311);
+
+ private final int mId;
+
+ ScreenshotEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+
+ static ScreenshotEvent getScreenshotSource(int source) {
+ switch (source) {
+ case SCREENSHOT_GLOBAL_ACTIONS:
+ return ScreenshotEvent.SCREENSHOT_REQUESTED_GLOBAL_ACTIONS;
+ case SCREENSHOT_KEY_CHORD:
+ return ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_CHORD;
+ case SCREENSHOT_KEY_OTHER:
+ return ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER;
+ case SCREENSHOT_OVERVIEW:
+ return ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW;
+ case SCREENSHOT_ACCESSIBILITY_ACTIONS:
+ return ScreenshotEvent.SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS;
+ case SCREENSHOT_OTHER:
+ default:
+ return ScreenshotEvent.SCREENSHOT_REQUESTED_OTHER;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 811a8d936b77..fbcd6ba0ff47 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -32,7 +32,6 @@ import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Picture;
-import android.net.Uri;
import android.os.UserHandle;
import android.util.DisplayMetrics;
import android.view.WindowManager;
@@ -42,8 +41,6 @@ import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.util.NotificationChannels;
-import java.util.List;
-
import javax.inject.Inject;
/**
@@ -185,23 +182,20 @@ public class ScreenshotNotificationsController {
/**
* Shows a notification with the saved screenshot and actions that can be taken with it.
*
- * @param imageUri URI for the saved image
- * @param actions a list of notification actions which can be taken
+ * @param actionData SavedImageData struct with image URI and actions
*/
public void showScreenshotActionsNotification(
- Uri imageUri,
- List<Notification.Action> smartActions,
- List<Notification.Action> actions) {
- for (Notification.Action action : actions) {
- mNotificationBuilder.addAction(action);
- }
- for (Notification.Action smartAction : smartActions) {
+ GlobalScreenshot.SavedImageData actionData) {
+ mNotificationBuilder.addAction(actionData.shareAction);
+ mNotificationBuilder.addAction(actionData.editAction);
+ mNotificationBuilder.addAction(actionData.deleteAction);
+ for (Notification.Action smartAction : actionData.smartActions) {
mNotificationBuilder.addAction(smartAction);
}
// Create the intent to show the screenshot in gallery
Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(imageUri, "image/png");
+ launchIntent.setDataAndType(actionData.uri, "image/png");
launchIntent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 8b8b6f8071e1..a6e083b04ea3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -32,6 +32,9 @@ import android.os.UserManager;
import android.util.Log;
import android.view.WindowManager;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.ScreenshotHelper;
+
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -42,6 +45,7 @@ public class TakeScreenshotService extends Service {
private final GlobalScreenshot mScreenshot;
private final GlobalScreenshotLegacy mScreenshotLegacy;
private final UserManager mUserManager;
+ private final UiEventLogger mUiEventLogger;
private Handler mHandler = new Handler(Looper.myLooper()) {
@Override
@@ -64,14 +68,22 @@ public class TakeScreenshotService extends Service {
return;
}
- // TODO (mkephart): clean up once notifications flow is fully deprecated
+ // TODO: clean up once notifications flow is fully deprecated
boolean useCornerFlow = true;
+
+ ScreenshotHelper.ScreenshotRequest screenshotRequest =
+ (ScreenshotHelper.ScreenshotRequest) msg.obj;
+
+ mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()));
+
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (useCornerFlow) {
mScreenshot.takeScreenshot(finisher);
} else {
- mScreenshotLegacy.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
+ mScreenshotLegacy.takeScreenshot(
+ finisher, screenshotRequest.getHasStatusBar(),
+ screenshotRequest.getHasNavBar());
}
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
@@ -79,17 +91,15 @@ public class TakeScreenshotService extends Service {
mScreenshot.takeScreenshotPartial(finisher);
} else {
mScreenshotLegacy.takeScreenshotPartial(
- finisher, msg.arg1 > 0, msg.arg2 > 0);
+ finisher, screenshotRequest.getHasStatusBar(),
+ screenshotRequest.getHasNavBar());
}
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
- Bitmap screenshot = msg.getData().getParcelable(
- WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP);
- Rect screenBounds = msg.getData().getParcelable(
- WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS);
- Insets insets = msg.getData().getParcelable(
- WindowManager.PARCEL_KEY_SCREENSHOT_INSETS);
- int taskId = msg.getData().getInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID);
+ Bitmap screenshot = screenshotRequest.getBitmap();
+ Rect screenBounds = screenshotRequest.getBoundsInScreen();
+ Insets insets = screenshotRequest.getInsets();
+ int taskId = screenshotRequest.getTaskId();
if (useCornerFlow) {
mScreenshot.handleImageAsScreenshot(
screenshot, screenBounds, insets, taskId, finisher);
@@ -106,10 +116,12 @@ public class TakeScreenshotService extends Service {
@Inject
public TakeScreenshotService(GlobalScreenshot globalScreenshot,
- GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager) {
+ GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager,
+ UiEventLogger uiEventLogger) {
mScreenshot = globalScreenshot;
mScreenshotLegacy = globalScreenshotLegacy;
mUserManager = userManager;
+ mUiEventLogger = uiEventLogger;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 1b7524521d76..8c24c540e743 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NavigationBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarView;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.policy.BatteryController;
import javax.inject.Inject;
@@ -139,7 +140,8 @@ public class NavigationBarController implements Callbacks {
? Dependency.get(LightBarController.class)
: new LightBarController(context,
Dependency.get(DarkIconDispatcher.class),
- Dependency.get(BatteryController.class));
+ Dependency.get(BatteryController.class),
+ Dependency.get(NavigationModeController.class));
navBar.setLightBarController(lightBarController);
// TODO(b/118592525): to support multi-display, we start to add something which is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 7d422e3c15a2..f9119c7a010f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -53,11 +53,13 @@ import android.view.WindowManagerGlobal;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -74,7 +76,7 @@ import java.util.concurrent.Executor;
/**
* Utility class to handle edge swipes for back gesture
*/
-public class EdgeBackGestureHandler implements DisplayListener,
+public class EdgeBackGestureHandler extends CurrentUserTracker implements DisplayListener,
PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> {
private static final String TAG = "EdgeBackGestureHandler";
@@ -165,6 +167,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
private boolean mIsGesturalModeEnabled;
private boolean mIsEnabled;
private boolean mIsNavBarShownTransiently;
+ private boolean mIsBackGestureAllowed;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
@@ -200,7 +203,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
SysUiState sysUiFlagContainer, PluginManager pluginManager) {
- final Resources res = context.getResources();
+ super(Dependency.get(BroadcastDispatcher.class));
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
@@ -216,20 +219,30 @@ public class EdgeBackGestureHandler implements DisplayListener,
ViewConfiguration.getLongPressTimeout());
mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
- mContext.getMainThreadHandler(), mContext, () -> updateCurrentUserResources(res));
+ mContext.getMainThreadHandler(), mContext, this::updateCurrentUserResources);
- updateCurrentUserResources(res);
+ updateCurrentUserResources();
sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
- public void updateCurrentUserResources(Resources res) {
+ public void updateCurrentUserResources() {
+ Resources res = Dependency.get(NavigationModeController.class).getCurrentUserContext()
+ .getResources();
mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
+ mIsBackGestureAllowed =
+ !mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
mBottomGestureHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_gesture_height);
}
+ @Override
+ public void onUserSwitched(int newUserId) {
+ updateIsEnabled();
+ updateCurrentUserResources();
+ }
+
/**
* @see NavigationBarView#onAttachedToWindow()
*/
@@ -243,6 +256,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
Settings.Global.getUriFor(FIXED_ROTATION_TRANSFORM_SETTING_NAME),
false /* notifyForDescendants */, mFixedRotationObserver, UserHandle.USER_ALL);
updateIsEnabled();
+ startTracking();
}
/**
@@ -255,6 +269,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
}
mContext.getContentResolver().unregisterContentObserver(mFixedRotationObserver);
updateIsEnabled();
+ stopTracking();
}
private void setRotationCallbacks(boolean enable) {
@@ -269,10 +284,13 @@ public class EdgeBackGestureHandler implements DisplayListener,
}
}
- public void onNavigationModeChanged(int mode, Context currentUserContext) {
+ /**
+ * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged
+ */
+ public void onNavigationModeChanged(int mode) {
mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode);
updateIsEnabled();
- updateCurrentUserResources(currentUserContext.getResources());
+ updateCurrentUserResources();
}
public void onNavBarTransientStateChanged(boolean isTransient) {
@@ -363,6 +381,10 @@ public class EdgeBackGestureHandler implements DisplayListener,
updateDisplaySize();
}
+ public boolean isHandlingGestures() {
+ return mIsEnabled && mIsBackGestureAllowed;
+ }
+
private WindowManager.LayoutParams createLayoutParams() {
Resources resources = mContext.getResources();
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
@@ -469,9 +491,9 @@ public class EdgeBackGestureHandler implements DisplayListener,
mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
mLogGesture = false;
mInRejectedExclusion = false;
- mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
- && isWithinTouchRegion((int) ev.getX(), (int) ev.getY())
- && !mDisabledForQuickstep;
+ mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
+ && !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
+ && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
if (mAllowGesture) {
mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
mEdgeBackPlugin.onMotionEvent(ev);
@@ -599,6 +621,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
public void dump(PrintWriter pw) {
pw.println("EdgeBackGestureHandler:");
pw.println(" mIsEnabled=" + mIsEnabled);
+ pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed);
pw.println(" mAllowGesture=" + mAllowGesture);
pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep);
pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index d35e1e1e176a..3e5eb5fba8f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -33,6 +33,7 @@ import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.policy.BatteryController;
import java.io.FileDescriptor;
@@ -58,6 +59,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
private int mStatusBarMode;
private int mNavigationBarMode;
+ private int mNavigationMode;
private final Color mDarkModeColor;
/**
@@ -84,11 +86,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
@Inject
public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher,
- BatteryController batteryController) {
+ BatteryController batteryController, NavigationModeController navModeController) {
mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
mBatteryController = batteryController;
mBatteryController.addCallback(this);
+ mNavigationMode = navModeController.addListener((mode) -> {
+ mNavigationMode = mode;
+ });
}
public void setNavigationBar(LightBarTransitionsController navigationBar) {
@@ -234,7 +239,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
}
private void updateNavigation() {
- if (mNavigationBarController != null) {
+ if (mNavigationBarController != null
+ && !QuickStepContract.isGesturalMode(mNavigationMode)) {
mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 84aecd4e0759..2978772cac5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -123,7 +123,7 @@ public class NavigationBarView extends FrameLayout implements
private KeyButtonDrawable mRecentIcon;
private KeyButtonDrawable mDockedIcon;
- private final EdgeBackGestureHandler mEdgeBackGestureHandler;
+ private EdgeBackGestureHandler mEdgeBackGestureHandler;
private final DeadZone mDeadZone;
private boolean mDeadZoneConsuming = false;
private final NavigationBarTransitions mBarTransitions;
@@ -244,7 +244,7 @@ public class NavigationBarView extends FrameLayout implements
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
// When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
// gestural mode, the entire nav bar should be touchable.
- if (!isGesturalMode(mNavBarMode) || mImeVisible) {
+ if (!mEdgeBackGestureHandler.isHandlingGestures() || mImeVisible) {
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
return;
}
@@ -296,8 +296,6 @@ public class NavigationBarView extends FrameLayout implements
R.style.RotateButtonCCWStart90,
isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton);
- final ContextualButton backButton = new ContextualButton(R.id.back, 0);
-
mConfiguration = new Configuration();
mTmpLastConfiguration = new Configuration();
mConfiguration.updateFrom(context.getResources().getConfiguration());
@@ -305,7 +303,7 @@ public class NavigationBarView extends FrameLayout implements
mScreenPinningNotify = new ScreenPinningNotify(mContext);
mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class));
- mButtonDispatchers.put(R.id.back, backButton);
+ mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
@@ -659,7 +657,7 @@ public class NavigationBarView extends FrameLayout implements
boolean disableHomeHandle = disableRecent
&& ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
- boolean disableBack = !useAltBack && (isGesturalMode(mNavBarMode)
+ boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
|| ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0));
// When screen pinning, don't hide back and home when connected service or back and
@@ -686,9 +684,9 @@ public class NavigationBarView extends FrameLayout implements
}
}
- getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
- getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
- getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
+ getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
+ getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
+ getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
}
@@ -838,10 +836,9 @@ public class NavigationBarView extends FrameLayout implements
@Override
public void onNavigationModeChanged(int mode) {
- Context curUserCtx = Dependency.get(NavigationModeController.class).getCurrentUserContext();
mNavBarMode = mode;
mBarTransitions.onNavigationModeChanged(mNavBarMode);
- mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode, curUserCtx);
+ mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
@@ -864,6 +861,7 @@ public class NavigationBarView extends FrameLayout implements
@Override
public void onFinishInflate() {
+ super.onFinishInflate();
mNavigationInflaterView = findViewById(R.id.navigation_inflater);
mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index d24ccf343a3a..6061b1e73d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -17,9 +17,6 @@
package com.android.systemui.statusbar.phone;
import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.content.Intent.ACTION_PREFERRED_ACTIVITY_CHANGED;
-import static android.os.UserHandle.USER_CURRENT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
@@ -38,17 +35,14 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.util.Log;
-import android.util.SparseBooleanArray;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -70,104 +64,34 @@ public class NavigationModeController implements Dumpable {
private final Context mContext;
private Context mCurrentUserContext;
private final IOverlayManager mOverlayManager;
- private final DeviceProvisionedController mDeviceProvisionedController;
private final Executor mUiBgExecutor;
- private SparseBooleanArray mRestoreGesturalNavBarMode = new SparseBooleanArray();
-
private ArrayList<ModeChangedListener> mListeners = new ArrayList<>();
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case ACTION_OVERLAY_CHANGED:
if (DEBUG) {
Log.d(TAG, "ACTION_OVERLAY_CHANGED");
}
updateCurrentInteractionMode(true /* notify */);
- break;
- }
}
};
- private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedCallback =
- new DeviceProvisionedController.DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- if (DEBUG) {
- Log.d(TAG, "onDeviceProvisionedChanged: "
- + mDeviceProvisionedController.isDeviceProvisioned());
- }
- // Once the device has been provisioned, check if we can restore gestural nav
- restoreGesturalNavOverlayIfNecessary();
- }
-
- @Override
- public void onUserSetupChanged() {
- if (DEBUG) {
- Log.d(TAG, "onUserSetupChanged: "
- + mDeviceProvisionedController.isCurrentUserSetup());
- }
- // Once the user has been setup, check if we can restore gestural nav
- restoreGesturalNavOverlayIfNecessary();
- }
-
- @Override
- public void onUserSwitched() {
- if (DEBUG) {
- Log.d(TAG, "onUserSwitched: "
- + ActivityManagerWrapper.getInstance().getCurrentUserId());
- }
-
- // Update the nav mode for the current user
- updateCurrentInteractionMode(true /* notify */);
-
- // When switching users, defer enabling the gestural nav overlay until the user
- // is all set up
- deferGesturalNavOverlayIfNecessary();
- }
- };
-
@Inject
- public NavigationModeController(Context context,
- DeviceProvisionedController deviceProvisionedController,
- @UiBackground Executor uiBgExecutor) {
+ public NavigationModeController(Context context, @UiBackground Executor uiBgExecutor) {
mContext = context;
mCurrentUserContext = context;
mOverlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
mUiBgExecutor = uiBgExecutor;
- mDeviceProvisionedController = deviceProvisionedController;
- mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
overlayFilter.addDataScheme("package");
overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
- IntentFilter preferredActivityFilter = new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED);
- mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, preferredActivityFilter, null,
- null);
-
updateCurrentInteractionMode(false /* notify */);
-
- // Check if we need to defer enabling gestural nav
- deferGesturalNavOverlayIfNecessary();
- }
-
- private boolean setGestureModeOverlayForMainLauncher() {
- if (getCurrentInteractionMode(mCurrentUserContext) == NAV_BAR_MODE_GESTURAL) {
- // Already in gesture mode
- return true;
- }
-
- Log.d(TAG, "Switching system navigation to full-gesture mode:"
- + " contextUser="
- + mCurrentUserContext.getUserId());
-
- setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT);
- return true;
}
public void updateCurrentInteractionMode(boolean notify) {
@@ -176,10 +100,9 @@ public class NavigationModeController implements Dumpable {
if (mode == NAV_BAR_MODE_GESTURAL) {
switchToDefaultGestureNavOverlayIfNecessary();
}
- mUiBgExecutor.execute(() -> {
+ mUiBgExecutor.execute(() ->
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
- Secure.NAVIGATION_MODE, String.valueOf(mode));
- });
+ Secure.NAVIGATION_MODE, String.valueOf(mode)));
if (DEBUG) {
Log.e(TAG, "updateCurrentInteractionMode: mode=" + mode);
dumpAssetPaths(mCurrentUserContext);
@@ -230,61 +153,11 @@ public class NavigationModeController implements Dumpable {
}
}
- private void deferGesturalNavOverlayIfNecessary() {
- final int userId = mDeviceProvisionedController.getCurrentUser();
- mRestoreGesturalNavBarMode.put(userId, false);
- if (mDeviceProvisionedController.isDeviceProvisioned()
- && mDeviceProvisionedController.isCurrentUserSetup()) {
- // User is already setup and device is provisioned, nothing to do
- if (DEBUG) {
- Log.d(TAG, "deferGesturalNavOverlayIfNecessary: device is provisioned and user is "
- + "setup");
- }
- return;
- }
-
- ArrayList<String> defaultOverlays = new ArrayList<>();
- try {
- defaultOverlays.addAll(Arrays.asList(mOverlayManager.getDefaultOverlayPackages()));
- } catch (RemoteException e) {
- Log.e(TAG, "deferGesturalNavOverlayIfNecessary: failed to fetch default overlays");
- }
- if (!defaultOverlays.contains(NAV_BAR_MODE_GESTURAL_OVERLAY)) {
- // No default gesture nav overlay
- if (DEBUG) {
- Log.d(TAG, "deferGesturalNavOverlayIfNecessary: no default gestural overlay, "
- + "default=" + defaultOverlays);
- }
- return;
- }
-
- // If the default is gestural, force-enable three button mode until the device is
- // provisioned
- setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT);
- mRestoreGesturalNavBarMode.put(userId, true);
-
- if (DEBUG) {
- Log.d(TAG, "deferGesturalNavOverlayIfNecessary: setting to 3 button mode");
- }
- }
-
- private void restoreGesturalNavOverlayIfNecessary() {
- if (DEBUG) {
- Log.d(TAG, "restoreGesturalNavOverlayIfNecessary: needs restore="
- + mRestoreGesturalNavBarMode);
- }
- final int userId = mDeviceProvisionedController.getCurrentUser();
- if (mRestoreGesturalNavBarMode.get(userId)) {
- // Restore the gestural state if necessary
- setGestureModeOverlayForMainLauncher();
- mRestoreGesturalNavBarMode.put(userId, false);
- }
- }
-
private void switchToDefaultGestureNavOverlayIfNecessary() {
final int userId = mCurrentUserContext.getUserId();
try {
- final IOverlayManager om = mOverlayManager;
+ final IOverlayManager om = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
final OverlayInfo info = om.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
if (info != null && !info.isEnabled()) {
// Enable the default gesture nav overlay, and move the back gesture inset scale to
@@ -309,20 +182,6 @@ public class NavigationModeController implements Dumpable {
}
}
- public void setModeOverlay(String overlayPkg, int userId) {
- mUiBgExecutor.execute(() -> {
- try {
- mOverlayManager.setEnabledExclusiveInCategory(overlayPkg, userId);
- if (DEBUG) {
- Log.d(TAG, "setModeOverlay: overlayPackage=" + overlayPkg
- + " userId=" + userId);
- }
- } catch (SecurityException | IllegalStateException | RemoteException e) {
- Log.e(TAG, "Failed to enable overlay " + overlayPkg + " for user " + userId);
- }
- });
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NavigationModeController:");
@@ -334,11 +193,6 @@ public class NavigationModeController implements Dumpable {
defaultOverlays = "failed_to_fetch";
}
pw.println(" defaultOverlays=" + defaultOverlays);
- pw.println(" restoreGesturalNavMode:");
- for (int i = 0; i < mRestoreGesturalNavBarMode.size(); i++) {
- pw.println(" userId=" + mRestoreGesturalNavBarMode.keyAt(i)
- + " shouldRestore=" + mRestoreGesturalNavBarMode.valueAt(i));
- }
dumpAssetPaths(mCurrentUserContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
index 1a6b415f87db..bf52a7ae2bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
@@ -148,11 +148,6 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
updateSamplingRect();
}
- private void postUpdateSamplingListener() {
- mHandler.removeCallbacks(mUpdateSamplingListener);
- mHandler.post(mUpdateSamplingListener);
- }
-
private void updateSamplingListener() {
boolean isSamplingEnabled = mSamplingEnabled
&& !mSamplingRequestBounds.isEmpty()
diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
index d65b285adb0c..8880df9959c1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
@@ -115,7 +115,9 @@ abstract class RelativeTouchListener : View.OnTouchListener {
performedLongClick = false
handler.postDelayed({
- performedLongClick = v.performLongClick()
+ if (v.isLongClickable) {
+ performedLongClick = v.performLongClick()
+ }
}, ViewConfiguration.getLongPressTimeout().toLong())
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
index 8625d63a3c7e..db08d64acc10 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -61,14 +61,17 @@ internal val animators = WeakHashMap<Any, PhysicsAnimator<*>>()
/**
* Default spring configuration to use for animations where stiffness and/or damping ratio
- * were not provided.
+ * were not provided, and a default spring was not set via [PhysicsAnimator.setDefaultSpringConfig].
*/
-private val defaultSpring = PhysicsAnimator.SpringConfig(
+private val globalDefaultSpring = PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_MEDIUM,
SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
-/** Default fling configuration to use for animations where friction was not provided. */
-private val defaultFling = PhysicsAnimator.FlingConfig(
+/**
+ * Default fling configuration to use for animations where friction was not provided, and a default
+ * fling config was not set via [PhysicsAnimator.setDefaultFlingConfig].
+ */
+private val globalDefaultFling = PhysicsAnimator.FlingConfig(
friction = 1f, min = -Float.MAX_VALUE, max = Float.MAX_VALUE)
/** Whether to log helpful debug information about animations. */
@@ -111,6 +114,12 @@ class PhysicsAnimator<T> private constructor (val target: T) {
/** End actions to run when all animations have completed. */
private val endActions = ArrayList<EndAction>()
+ /** SpringConfig to use by default for properties whose springs were not provided. */
+ private var defaultSpring: SpringConfig = globalDefaultSpring
+
+ /** FlingConfig to use by default for properties whose fling configs were not provided. */
+ private var defaultFling: FlingConfig = globalDefaultFling
+
/**
* Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
* the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
@@ -204,6 +213,19 @@ class PhysicsAnimator<T> private constructor (val target: T) {
}
/**
+ * Springs a property to a given value using the provided configuration options, and a start
+ * velocity of 0f.
+ *
+ * @see spring
+ */
+ fun spring(
+ property: FloatPropertyCompat<in T>,
+ toPosition: Float
+ ): PhysicsAnimator<T> {
+ return spring(property, toPosition, 0f)
+ }
+
+ /**
* Flings a property using the given start velocity, using a [FlingAnimation] configured using
* the provided configuration settings.
*
@@ -392,6 +414,14 @@ class PhysicsAnimator<T> private constructor (val target: T) {
return this
}
+ fun setDefaultSpringConfig(defaultSpring: SpringConfig) {
+ this.defaultSpring = defaultSpring
+ }
+
+ fun setDefaultFlingConfig(defaultFling: FlingConfig) {
+ this.defaultFling = defaultFling
+ }
+
/** Starts the animations! */
fun start() {
startAction()
@@ -752,7 +782,7 @@ class PhysicsAnimator<T> private constructor (val target: T) {
) {
constructor() :
- this(defaultSpring.stiffness, defaultSpring.dampingRatio)
+ this(globalDefaultSpring.stiffness, globalDefaultSpring.dampingRatio)
constructor(stiffness: Float, dampingRatio: Float) :
this(stiffness = stiffness, dampingRatio = dampingRatio, startVelocity = 0f)
@@ -782,10 +812,10 @@ class PhysicsAnimator<T> private constructor (val target: T) {
internal var startVelocity: Float
) {
- constructor() : this(defaultFling.friction)
+ constructor() : this(globalDefaultFling.friction)
constructor(friction: Float) :
- this(friction, defaultFling.min, defaultFling.max)
+ this(friction, globalDefaultFling.min, globalDefaultFling.max)
constructor(friction: Float, min: Float, max: Float) :
this(friction, min, max, startVelocity = 0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 11649ca34709..ffe3cd5bd504 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -16,6 +16,10 @@
package com.android.systemui.screenshot;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -40,7 +44,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,9 +85,9 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
CompletableFuture<List<Notification.Action>> smartActionsFuture =
ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap,
smartActionsProvider, true, false);
- Assert.assertNotNull(smartActionsFuture);
+ assertNotNull(smartActionsFuture);
List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
- Assert.assertEquals(Collections.emptyList(), smartActions);
+ assertEquals(Collections.emptyList(), smartActions);
}
// Tests any exception thrown in waiting for smart actions future to complete does
@@ -99,7 +102,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
RuntimeException.class);
List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions(
"", "", smartActionsFuture, timeoutMs, mSmartActionsProvider);
- Assert.assertEquals(Collections.emptyList(), actions);
+ assertEquals(Collections.emptyList(), actions);
}
// Tests any exception thrown in notifying feedback does not affect regular screenshot flow.
@@ -123,9 +126,9 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
mSmartActionsProvider, true, true);
verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(),
eq(false));
- Assert.assertNotNull(smartActionsFuture);
+ assertNotNull(smartActionsFuture);
List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
- Assert.assertEquals(Collections.emptyList(), smartActions);
+ assertEquals(Collections.emptyList(), smartActions);
}
// Tests for a hardware bitmap, ScreenshotNotificationSmartActionsProvider is invoked once.
@@ -152,14 +155,14 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap,
actionsProvider,
true, true);
- Assert.assertNotNull(smartActionsFuture);
+ assertNotNull(smartActionsFuture);
List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
- Assert.assertEquals(smartActions.size(), 0);
+ assertEquals(smartActions.size(), 0);
}
- // Tests for notification action extras.
+ // Tests for share action extras
@Test
- public void testNotificationActionExtras() {
+ public void testShareActionExtras() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
@@ -169,35 +172,70 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.finisher = null;
data.mActionsReadyListener = null;
- data.createDeleteAction = true;
SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
- List<Notification.Action> actions = task.populateNotificationActions(
- mContext, mContext.getResources(),
+
+ Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png"));
- Assert.assertEquals(actions.size(), 3);
- boolean isShareFound = false;
- boolean isEditFound = false;
- boolean isDeleteFound = false;
- for (Notification.Action action : actions) {
- Intent intent = action.actionIntent.getIntent();
- Assert.assertNotNull(intent);
- Bundle bundle = intent.getExtras();
- Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
- Assert.assertTrue(
- bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
-
- if (action.title.equals(GlobalScreenshot.ACTION_TYPE_DELETE)) {
- isDeleteFound = intent.getAction() == null;
- } else if (action.title.equals(GlobalScreenshot.ACTION_TYPE_EDIT)) {
- isEditFound = Intent.ACTION_EDIT.equals(intent.getAction());
- } else if (action.title.equals(GlobalScreenshot.ACTION_TYPE_SHARE)) {
- isShareFound = Intent.ACTION_SEND.equals(intent.getAction());
- }
+ Intent intent = shareAction.actionIntent.getIntent();
+ assertNotNull(intent);
+ Bundle bundle = intent.getExtras();
+ assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
+ assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
+ assertEquals(GlobalScreenshot.ACTION_TYPE_SHARE, shareAction.title);
+ assertEquals(Intent.ACTION_SEND, intent.getAction());
+ }
+
+ // Tests for edit action extras
+ @Test
+ public void testEditActionExtras() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
}
- Assert.assertTrue(isEditFound);
- Assert.assertTrue(isDeleteFound);
- Assert.assertTrue(isShareFound);
+ GlobalScreenshot.SaveImageInBackgroundData
+ data = new GlobalScreenshot.SaveImageInBackgroundData();
+ data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+ data.finisher = null;
+ data.mActionsReadyListener = null;
+ SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+
+ Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
+ Uri.parse("Screenshot_123.png"));
+
+ Intent intent = editAction.actionIntent.getIntent();
+ assertNotNull(intent);
+ Bundle bundle = intent.getExtras();
+ assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
+ assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
+ assertEquals(GlobalScreenshot.ACTION_TYPE_EDIT, editAction.title);
+ assertEquals(Intent.ACTION_EDIT, intent.getAction());
+ }
+
+ // Tests for share action extras
+ @Test
+ public void testDeleteActionExtras() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ GlobalScreenshot.SaveImageInBackgroundData
+ data = new GlobalScreenshot.SaveImageInBackgroundData();
+ data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+ data.finisher = null;
+ data.mActionsReadyListener = null;
+ SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
+
+ Notification.Action deleteAction = task.createDeleteAction(mContext,
+ mContext.getResources(),
+ Uri.parse("Screenshot_123.png"));
+
+ Intent intent = deleteAction.actionIntent.getIntent();
+ assertNotNull(intent);
+ Bundle bundle = intent.getExtras();
+ assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID));
+ assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED));
+ assertEquals(deleteAction.title, GlobalScreenshot.ACTION_TYPE_DELETE);
+ assertNull(intent.getAction());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index 8ab660c3fca3..3e469073694f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -56,7 +56,7 @@ public class LightBarControllerTest extends SysuiTestCase {
when(mStatusBarIconController.getTransitionsController()).thenReturn(
mLightBarTransitionsController);
mLightBarController = new LightBarController(mContext, mStatusBarIconController,
- mock(BatteryController.class));
+ mock(BatteryController.class), mock(NavigationModeController.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
index 27a50027cfe2..14c6e9f9624d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -50,9 +51,11 @@ public class NavigationBarTransitionsTest extends SysuiTestCase {
mDependency.injectMockDependency(IWindowManager.class);
mDependency.injectMockDependency(AssistManager.class);
mDependency.injectMockDependency(OverviewProxyService.class);
- mDependency.injectMockDependency(NavigationModeController.class);
mDependency.injectMockDependency(StatusBarStateController.class);
mDependency.injectMockDependency(KeyguardStateController.class);
+ doReturn(mContext)
+ .when(mDependency.injectMockDependency(NavigationModeController.class))
+ .getCurrentUserContext();
NavigationBarView navBar = spy(new NavigationBarView(mContext, null));
when(navBar.getCurrentView()).thenReturn(navBar);
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index b9e3050275ad..a1fc3fa1857d 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
+
import android.accessibilityservice.AccessibilityService;
import android.app.PendingIntent;
import android.app.RemoteAction;
@@ -399,7 +401,8 @@ public class SystemActionPerformer {
ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null)
? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext);
screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
- true, true, new Handler(Looper.getMainLooper()), null);
+ true, true, SCREENSHOT_ACCESSIBILITY_ACTIONS,
+ new Handler(Looper.getMainLooper()), null);
return true;
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b27c5d54a6fb..35089d6f5de7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -117,6 +117,7 @@ import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* A session for a given activity.
@@ -3079,19 +3080,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final boolean isWhitelisted = mService
.isWhitelistedForAugmentedAutofillLocked(mComponentName);
- final String historyItem =
- "aug:id=" + id + " u=" + uid + " m=" + mode
- + " a=" + ComponentName.flattenToShortString(mComponentName)
- + " f=" + mCurrentViewId
- + " s=" + remoteService.getComponentName()
- + " w=" + isWhitelisted;
- mService.getMaster().logRequestLocked(historyItem);
-
if (!isWhitelisted) {
if (sVerbose) {
Slog.v(TAG, "triggerAugmentedAutofillLocked(): "
+ ComponentName.flattenToShortString(mComponentName) + " not whitelisted ");
}
+ logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
+ mCurrentViewId, isWhitelisted, /*isInline*/null);
return null;
}
@@ -3116,24 +3111,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillId focusedId = mCurrentViewId;
+ final Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsResponseCallback =
+ response -> {
+ synchronized (mLock) {
+ return mInlineSessionController.onInlineSuggestionsResponseLocked(
+ focusedId, response);
+ }
+ };
final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill =
(inlineSuggestionsRequest) -> {
- remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName,
- AutofillId.withoutSession(focusedId),
- currentValue, inlineSuggestionsRequest,
- /*inlineSuggestionsCallback=*/
- response -> {
- synchronized (mLock) {
- return mInlineSessionController
- .onInlineSuggestionsResponseLocked(
- focusedId, response);
- }
- },
- /*onErrorCallback=*/ () -> {
- synchronized (mLock) {
- cancelAugmentedAutofillLocked();
- }
- }, mService.getRemoteInlineSuggestionRenderServiceLocked());
+ synchronized (mLock) {
+ logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
+ focusedId, isWhitelisted, inlineSuggestionsRequest != null);
+ remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName,
+ AutofillId.withoutSession(focusedId), currentValue,
+ inlineSuggestionsRequest, inlineSuggestionsResponseCallback,
+ /*onErrorCallback=*/ () -> {
+ synchronized (mLock) {
+ cancelAugmentedAutofillLocked();
+ }
+ }, mService.getRemoteInlineSuggestionRenderServiceLocked());
+ }
};
// When the inline suggestion render service is available, there are 2 cases when
@@ -3150,9 +3148,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback(
(extras) -> {
- mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
- focusedId, /*requestConsumer=*/ requestAugmentedAutofill,
- extras);
+ synchronized (mLock) {
+ mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
+ focusedId, /*requestConsumer=*/ requestAugmentedAutofill,
+ extras);
+ }
}, mHandler));
} else {
requestAugmentedAutofill.accept(
@@ -3165,6 +3165,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
+ private void logAugmentedAutofillRequestLocked(int mode,
+ ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted,
+ Boolean isInline) {
+ final String historyItem =
+ "aug:id=" + id + " u=" + uid + " m=" + mode
+ + " a=" + ComponentName.flattenToShortString(mComponentName)
+ + " f=" + focusedId
+ + " s=" + augmentedRemoteServiceName
+ + " w=" + isWhitelisted
+ + " i=" + isInline;
+ mService.getMaster().logRequestLocked(historyItem);
+ }
+
+ @GuardedBy("mLock")
private void cancelAugmentedAutofillLocked() {
final RemoteAugmentedAutofillService remoteService = mService
.getRemoteAugmentedAutofillServiceLocked();
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 7c47cf0450d4..d36d038058f1 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -612,7 +612,7 @@ public class AppsFilter {
}
final int insert = ~loc;
System.arraycopy(appIds, insert, buffer, 0, whitelistSize - insert);
- appIds[insert] = existingUid;
+ appIds[insert] = existingAppId;
System.arraycopy(buffer, 0, appIds, insert + 1, whitelistSize - insert);
whitelistSize++;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c716fce8a86d..4561d2e7a8b2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -104,6 +104,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
@@ -4516,6 +4517,8 @@ public class UserManagerService extends IUserManager.Stub {
switch(cmd) {
case "list":
return runList(pw, shell);
+ case "list-missing-system-packages":
+ return runListMissingSystemPackages(pw, shell);
default:
return shell.handleDefaultCommands(cmd);
}
@@ -4582,6 +4585,30 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ private int runListMissingSystemPackages(PrintWriter pw, Shell shell) {
+ boolean verbose = false;
+ boolean force = false;
+ String opt;
+ while ((opt = shell.getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ verbose = true;
+ break;
+ case "--force":
+ force = true;
+ break;
+ default:
+ pw.println("Invalid option: " + opt);
+ return -1;
+ }
+ }
+
+ try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) {
+ mSystemPackageInstaller.dumpMissingSystemPackages(ipw, force, verbose);
+ }
+ return 0;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
@@ -5154,6 +5181,9 @@ public class UserManagerService extends IUserManager.Stub {
pw.println("");
pw.println(" list [-v] [-all]");
pw.println(" Prints all users on the system.");
+ pw.println(" list-missing-system-packages [-v] [--force]");
+ pw.println(" Prints all system packages that were not explicitly configured to be "
+ + "installed.");
}
}
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 85c2306f6d89..cd1087f5fcd7 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -22,15 +22,16 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -38,7 +39,9 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -263,37 +266,85 @@ class UserSystemPackageInstaller {
if (!isLogMode(mode) && !isEnforceMode(mode)) {
return;
}
- Slog.v(TAG, "Checking that all system packages are whitelisted.");
+ final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode);
+ final int size = warnings.size();
+ if (size == 0) {
+ Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): no warnings");
+ return;
+ }
+
+ if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
+ // Only shows whether all whitelisted packages are indeed on the system.
+ for (int i = 0; i < size; i++) {
+ final Pair<Boolean, String> pair = warnings.get(i);
+ final boolean isSevere = pair.first;
+ if (!isSevere) {
+ final String msg = pair.second;
+ Slog.w(TAG, msg);
+ }
+ }
+ return;
+ }
+
+ Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): " + size + " warnings");
+ boolean doWtf = !isImplicitWhitelistMode(mode);
+ for (int i = 0; i < size; i++) {
+ final Pair<Boolean, String> pair = warnings.get(i);
+ final boolean isSevere = pair.first;
+ final String msg = pair.second;
+ if (isSevere) {
+ if (doWtf) {
+ Slog.wtf(TAG, msg);
+ } else {
+ Slog.e(TAG, msg);
+ }
+ } else {
+ Slog.w(TAG, msg);
+ }
+ }
+ }
+
+ // TODO: method below was created to refactor the one-time logging logic so it can be used on
+ // dump / cmd as well. It could to be further refactored (for example, creating a new
+ // structure for the warnings so it doesn't need a Pair).
+ /**
+ * Gets warnings for system user whitelisting.
+ *
+ * @return list of warnings, where {@code Pair.first} is the severity ({@code true} for WTF,
+ * {@code false} for WARN) and {@code Pair.second} the message.
+ */
+ @NonNull
+ private List<Pair<Boolean, String>> checkSystemPackagesWhitelistWarnings(
+ @PackageWhitelistMode int mode) {
final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
- PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ final List<Pair<Boolean, String>> warnings = new ArrayList<>();
+ final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
// Check whether all whitelisted packages are indeed on the system.
+ final String notPresentFmt = "%s is whitelisted but not present.";
+ final String notSystemFmt = "%s is whitelisted and present but not a system package.";
for (String pkgName : allWhitelistedPackages) {
- AndroidPackage pkg = pmInt.getPackage(pkgName);
+ final AndroidPackage pkg = pmInt.getPackage(pkgName);
if (pkg == null) {
- Slog.w(TAG, pkgName + " is whitelisted but not present.");
+ warnings.add(new Pair<>(false, String.format(notPresentFmt, pkgName)));
} else if (!pkg.isSystem()) {
- Slog.w(TAG, pkgName + " is whitelisted and present but not a system package.");
+ warnings.add(new Pair<>(false, String.format(notSystemFmt, pkgName)));
}
}
// Check whether all system packages are indeed whitelisted.
- if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
- return;
- }
- final boolean doWtf = isEnforceMode(mode);
+ final String logMessageFmt = "System package %s is not whitelisted using "
+ + "'install-in-user-type' in SystemConfig for any user types!";
+ final boolean isSevere = isEnforceMode(mode);
pmInt.forEachPackage(pkg -> {
- if (pkg.isSystem() && !allWhitelistedPackages.contains(pkg.getManifestPackageName())) {
- final String msg = "System package " + pkg.getManifestPackageName()
- + " is not whitelisted using 'install-in-user-type' in SystemConfig "
- + "for any user types!";
- if (doWtf) {
- Slog.wtf(TAG, msg);
- } else {
- Slog.e(TAG, msg);
- }
+ if (!pkg.isSystem()) return;
+ final String pkgName = pkg.getManifestPackageName();
+ if (!allWhitelistedPackages.contains(pkgName)) {
+ warnings.add(new Pair<>(isSevere, String.format(logMessageFmt, pkgName)));
}
});
+
+ return warnings;
}
/** Whether to only install system packages in new users for which they are whitelisted. */
@@ -602,32 +653,45 @@ class UserSystemPackageInstaller {
}
void dump(PrintWriter pw) {
- final String prefix = " ";
- final String prefix2 = prefix + prefix;
+ try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) {
+ dumpIndented(ipw);
+ }
+ }
+
+ private void dumpIndented(IndentingPrintWriter pw) {
final int mode = getWhitelistMode();
pw.println("Whitelisted packages per user type");
- pw.print(prefix); pw.print("Mode: ");
+
+ pw.increaseIndent();
+ pw.print("Mode: ");
pw.print(mode);
pw.print(isEnforceMode(mode) ? " (enforced)" : "");
pw.print(isLogMode(mode) ? " (logged)" : "");
pw.print(isImplicitWhitelistMode(mode) ? " (implicit)" : "");
pw.print(isIgnoreOtaMode(mode) ? " (ignore OTAs)" : "");
pw.println();
+ pw.decreaseIndent();
- pw.print(prefix); pw.println("Legend");
+ pw.increaseIndent();
+ pw.println("Legend");
+ pw.increaseIndent();
for (int idx = 0; idx < mUserTypes.length; idx++) {
- pw.print(prefix2); pw.println(idx + " -> " + mUserTypes[idx]);
+ pw.println(idx + " -> " + mUserTypes[idx]);
}
+ pw.decreaseIndent(); pw.decreaseIndent();
+ pw.increaseIndent();
final int size = mWhitelistedPackagesForUserTypes.size();
if (size == 0) {
- pw.print(prefix); pw.println("No packages");
+ pw.println("No packages");
+ pw.decreaseIndent();
return;
}
- pw.print(prefix); pw.print(size); pw.println(" packages:");
+ pw.print(size); pw.println(" packages:");
+ pw.increaseIndent();
for (int pkgIdx = 0; pkgIdx < size; pkgIdx++) {
final String pkgName = mWhitelistedPackagesForUserTypes.keyAt(pkgIdx);
- pw.print(prefix2); pw.print(pkgName); pw.print(": ");
+ pw.print(pkgName); pw.print(": ");
final long userTypesBitSet = mWhitelistedPackagesForUserTypes.valueAt(pkgIdx);
for (int idx = 0; idx < mUserTypes.length; idx++) {
if ((userTypesBitSet & (1 << idx)) != 0) {
@@ -636,5 +700,40 @@ class UserSystemPackageInstaller {
}
pw.println();
}
+ pw.decreaseIndent(); pw.decreaseIndent();
+
+ pw.increaseIndent();
+ dumpMissingSystemPackages(pw, /* force= */ true, /* verbose= */ true);
+ pw.decreaseIndent();
+ }
+
+ void dumpMissingSystemPackages(IndentingPrintWriter pw, boolean force, boolean verbose) {
+ final int mode = getWhitelistMode();
+ final boolean show = force || (isEnforceMode(mode) && !isImplicitWhitelistMode(mode));
+ if (!show) return;
+
+ final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode);
+ final int size = warnings.size();
+
+ if (size == 0) {
+ if (verbose) {
+ pw.println("All system packages are accounted for");
+ }
+ return;
+ }
+
+ if (verbose) {
+ pw.print(size); pw.println(" warnings for system user:");
+ pw.increaseIndent();
+ }
+ for (int i = 0; i < size; i++) {
+ final Pair<Boolean, String> pair = warnings.get(i);
+ final String lvl = pair.first ? "WTF" : "WARN";
+ final String msg = pair.second;
+ pw.print(lvl); pw.print(": "); pw.println(msg);
+ }
+ if (verbose) {
+ pw.decreaseIndent();
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4624e9ea0209..2f84a99774f4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -62,6 +62,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManagerGlobal.ADD_OKAY;
@@ -1328,6 +1330,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mScreenshotChordVolumeDownKeyConsumed = true;
cancelPendingPowerKeyAction();
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
+ mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
}
}
@@ -1411,14 +1414,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private class ScreenshotRunnable implements Runnable {
private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
+ private int mScreenshotSource = SCREENSHOT_KEY_OTHER;
public void setScreenshotType(int screenshotType) {
mScreenshotType = screenshotType;
}
+ public void setScreenshotSource(int screenshotSource) {
+ mScreenshotSource = screenshotSource;
+ }
+
@Override
public void run() {
- mDefaultDisplayPolicy.takeScreenshot(mScreenshotType);
+ mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);
}
}
@@ -2693,6 +2701,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
: TAKE_SCREENSHOT_FULLSCREEN;
mScreenshotRunnable.setScreenshotType(type);
+ mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
mHandler.post(mScreenshotRunnable);
return -1;
}
@@ -2709,6 +2718,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
if (down && repeatCount == 0) {
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
+ mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
mHandler.post(mScreenshotRunnable);
}
return -1;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
index a18b690f08cd..d98ad74a9d6b 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
@@ -23,6 +23,7 @@ import android.hardware.soundtrigger.V2_3.Properties;
import android.hardware.soundtrigger.V2_3.RecognitionConfig;
import android.os.IHwBinder;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Log;
import java.util.HashMap;
@@ -48,83 +49,130 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
@Override
public Properties getProperties() {
- return mUnderlying.getProperties();
+ try {
+ return mUnderlying.getProperties();
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
}
@Override
public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
int cookie) {
- int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback), cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
+ try {
+ int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback),
+ cookie);
+ synchronized (mModelStates) {
+ mModelStates.put(handle, false);
+ }
+ return handle;
+ } catch (RuntimeException e) {
+ throw handleException(e);
}
- return handle;
}
@Override
public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
int cookie) {
- int handle = mUnderlying.loadPhraseSoundModel(soundModel, new CallbackEnforcer(callback),
- cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
+ try {
+ int handle = mUnderlying.loadPhraseSoundModel(soundModel,
+ new CallbackEnforcer(callback),
+ cookie);
+ synchronized (mModelStates) {
+ mModelStates.put(handle, false);
+ }
+ return handle;
+ } catch (RuntimeException e) {
+ throw handleException(e);
}
- return handle;
}
@Override
public void unloadSoundModel(int modelHandle) {
- mUnderlying.unloadSoundModel(modelHandle);
- synchronized (mModelStates) {
- mModelStates.remove(modelHandle);
+ try {
+ mUnderlying.unloadSoundModel(modelHandle);
+ synchronized (mModelStates) {
+ mModelStates.remove(modelHandle);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
}
}
@Override
public void stopRecognition(int modelHandle) {
- mUnderlying.stopRecognition(modelHandle);
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, false);
+ try {
+ mUnderlying.stopRecognition(modelHandle);
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, false);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
}
}
@Override
public void stopAllRecognitions() {
- mUnderlying.stopAllRecognitions();
- synchronized (mModelStates) {
- for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
- entry.setValue(false);
+ try {
+ mUnderlying.stopAllRecognitions();
+ synchronized (mModelStates) {
+ for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
+ entry.setValue(false);
+ }
}
+ } catch (RuntimeException e) {
+ throw handleException(e);
}
}
@Override
public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
int cookie) {
- mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback), cookie);
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, true);
+ try {
+ mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback),
+ cookie);
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, true);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
}
}
@Override
public void getModelState(int modelHandle) {
- mUnderlying.getModelState(modelHandle);
+ try {
+ mUnderlying.getModelState(modelHandle);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
}
@Override
public int getModelParameter(int modelHandle, int param) {
- return mUnderlying.getModelParameter(modelHandle, param);
+ try {
+ return mUnderlying.getModelParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
}
@Override
public void setModelParameter(int modelHandle, int param, int value) {
- mUnderlying.setModelParameter(modelHandle, param, value);
+ try {
+ mUnderlying.setModelParameter(modelHandle, param, value);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
}
@Override
public ModelParameterRange queryParameter(int modelHandle, int param) {
- return mUnderlying.queryParameter(modelHandle, param);
+ try {
+ return mUnderlying.queryParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
}
@Override
@@ -142,6 +190,17 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
return mUnderlying.interfaceDescriptor();
}
+ private static RuntimeException handleException(RuntimeException e) {
+ Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ rebootHal();
+ throw e;
+ }
+
+ private static void rebootHal() {
+ // This property needs to be defined in an init.rc script and trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ }
+
private class CallbackEnforcer implements Callback {
private final Callback mUnderlying;
@@ -157,6 +216,8 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
synchronized (mModelStates) {
if (!mModelStates.getOrDefault(model, false)) {
Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ rebootHal();
+ return;
}
if (event.header.status
!= android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
@@ -173,6 +234,8 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
synchronized (mModelStates) {
if (!mModelStates.getOrDefault(model, false)) {
Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ rebootHal();
+ return;
}
if (event.common.header.status
!= android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 635cb613b934..4b464d2d3e04 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -27,9 +27,7 @@ import android.media.soundtrigger_middleware.PhraseSoundModel;
import android.media.soundtrigger_middleware.RecognitionConfig;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.os.Parcel;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.util.Log;
import com.android.server.SystemService;
@@ -101,28 +99,6 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
}
}
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- return super.onTransact(code, data, reply, flags);
- } catch (InternalServerError e) {
- if (e.getCause() instanceof HalException) {
- // We recover from any sort of HAL failure by rebooting the HAL process.
- // This will likely reboot more than just the sound trigger HAL.
- // The rest of the system should be able to tolerate that.
- rebootHal();
- }
- throw e;
- }
- }
-
- private static void rebootHal() {
- Log.i(TAG, "Rebooting the sound trigger HAL");
- // This property needs to be defined in an init.rc script and trigger a HAL reboot.
- SystemProperties.set("sys.audio.restart.hal", "1");
- }
-
private final static class ModuleService extends ISoundTriggerModule.Stub {
private final ISoundTriggerModule mDelegate;
@@ -182,22 +158,6 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
public void detach() throws RemoteException {
mDelegate.detach();
}
-
- @Override
- public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
- throws RemoteException {
- try {
- return super.onTransact(code, data, reply, flags);
- } catch (InternalServerError e) {
- if (e.getCause() instanceof HalException) {
- // We recover from any sort of HAL failure by rebooting the HAL process.
- // This will likely reboot more than just the sound trigger HAL.
- // The rest of the system should be able to tolerate that.
- rebootHal();
- }
- throw e;
- }
- }
}
/**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
new file mode 100644
index 000000000000..54ad1d268e56
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tv.tunerresourcemanager;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A Cas resource object used by the Tuner Resource Manager to record the cas
+ * information.
+ *
+ * @hide
+ */
+public final class CasResource {
+
+ private final int mSystemId;
+
+ private int mMaxSessionNum;
+
+ private int mAvailableSessionNum;
+
+ /**
+ * The owner clients' ids when part of the Cas is occupied.
+ */
+ private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>();
+
+ private CasResource(Builder builder) {
+ this.mSystemId = builder.mSystemId;
+ this.mMaxSessionNum = builder.mMaxSessionNum;
+ this.mAvailableSessionNum = builder.mMaxSessionNum;
+ }
+
+ public int getSystemId() {
+ return mSystemId;
+ }
+
+ public int getMaxSessionNum() {
+ return mMaxSessionNum;
+ }
+
+ public int getUsedSessionNum() {
+ return (mMaxSessionNum - mAvailableSessionNum);
+ }
+
+ public boolean isFullyUsed() {
+ return mAvailableSessionNum == 0;
+ }
+
+ /**
+ * Update max session number.
+ *
+ * @param maxSessionNum the new max session num.
+ */
+ public void updateMaxSessionNum(int maxSessionNum) {
+ mAvailableSessionNum = Math.max(
+ 0, mAvailableSessionNum + (maxSessionNum - mMaxSessionNum));
+ mMaxSessionNum = maxSessionNum;
+ }
+
+ /**
+ * Set an owner for the cas
+ *
+ * @param ownerId the client id of the owner.
+ */
+ public void setOwner(int ownerId) {
+ int sessionNum = mOwnerClientIdsToSessionNum.get(ownerId) == null
+ ? 1 : (mOwnerClientIdsToSessionNum.get(ownerId) + 1);
+ mOwnerClientIdsToSessionNum.put(ownerId, sessionNum);
+ mAvailableSessionNum--;
+ }
+
+ /**
+ * Remove an owner of the Cas.
+ *
+ * @param ownerId the removing client id of the owner.
+ */
+ public void removeOwner(int ownerId) {
+ mAvailableSessionNum += mOwnerClientIdsToSessionNum.get(ownerId);
+ mOwnerClientIdsToSessionNum.remove(ownerId);
+ }
+
+ public Set<Integer> getOwnerClientIds() {
+ return mOwnerClientIdsToSessionNum.keySet();
+ }
+
+ @Override
+ public String toString() {
+ return "CasResource[systemId=" + this.mSystemId
+ + ", isFullyUsed=" + (this.mAvailableSessionNum == 0)
+ + ", maxSessionNum=" + this.mMaxSessionNum
+ + ", ownerClients=" + ownersMapToString() + "]";
+ }
+
+ /**
+ * Builder class for {@link CasResource}.
+ */
+ public static class Builder {
+
+ private int mSystemId;
+ private int mMaxSessionNum;
+
+ Builder(int systemId) {
+ this.mSystemId = systemId;
+ }
+
+ /**
+ * Builder for {@link CasResource}.
+ *
+ * @param maxSessionNum the max session num the current Cas has.
+ */
+ public Builder maxSessionNum(int maxSessionNum) {
+ this.mMaxSessionNum = maxSessionNum;
+ return this;
+ }
+
+ /**
+ * Build a {@link CasResource}.
+ *
+ * @return {@link CasResource}.
+ */
+ public CasResource build() {
+ CasResource cas = new CasResource(this);
+ return cas;
+ }
+ }
+
+ private String ownersMapToString() {
+ StringBuilder string = new StringBuilder("{");
+ for (int clienId : mOwnerClientIdsToSessionNum.keySet()) {
+ string.append(" clientId=")
+ .append(clienId)
+ .append(", owns session num=")
+ .append(mOwnerClientIdsToSessionNum.get(clienId))
+ .append(",");
+ }
+ return string.append("}").toString();
+ }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 4cdc17292ac4..2b0fe8a2602b 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -27,6 +27,7 @@ import java.util.Set;
public final class ClientProfile {
public static final int INVALID_GROUP_ID = -1;
+ public static final int INVALID_RESOURCE_ID = -1;
/**
* Client id sent to the client when registering with
@@ -56,7 +57,6 @@ public final class ClientProfile {
* also lose their resources.
*/
private int mGroupId = INVALID_GROUP_ID;
-
/**
* Optional nice value for TRM to reduce client’s priority.
*/
@@ -73,6 +73,11 @@ public final class ClientProfile {
private Set<Integer> mUsingLnbIds = new HashSet<>();
/**
+ * List of the Cas system ids that are used by the current client.
+ */
+ private int mUsingCasSystemId = INVALID_RESOURCE_ID;
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -172,11 +177,32 @@ public final class ClientProfile {
}
/**
+ * Set when the client starts to use a Cas system.
+ *
+ * @param casSystemId cas being used.
+ */
+ public void useCas(int casSystemId) {
+ mUsingCasSystemId = casSystemId;
+ }
+
+ public int getInUseCasSystemId() {
+ return mUsingCasSystemId;
+ }
+
+ /**
+ * Called when the client released a Cas System.
+ */
+ public void releaseCas() {
+ mUsingCasSystemId = INVALID_RESOURCE_ID;
+ }
+
+ /**
* Called to reclaim all the resources being used by the current client.
*/
public void reclaimAllResources() {
mUsingFrontendIds.clear();
mUsingLnbIds.clear();
+ mUsingCasSystemId = INVALID_RESOURCE_ID;
}
@Override
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 7231813dd949..2f70840cfc8b 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -65,6 +65,8 @@ public class TunerResourceManagerService extends SystemService {
private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>();
// Map of the current available lnb resources
private Map<Integer, LnbResource> mLnbResources = new HashMap<>();
+ // Map of the current available Cas resources
+ private Map<Integer, CasResource> mCasResources = new HashMap<>();
@GuardedBy("mLock")
private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>();
@@ -158,10 +160,8 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void updateCasInfo(int casSystemId, int maxSessionNum) {
enforceTrmAccessPermission("updateCasInfo");
- if (DEBUG) {
- Slog.d(TAG,
- "updateCasInfo(casSystemId=" + casSystemId
- + ", maxSessionNum=" + maxSessionNum + ")");
+ synchronized (mLock) {
+ updateCasInfoInternal(casSystemId, maxSessionNum);
}
}
@@ -185,11 +185,11 @@ public class TunerResourceManagerService extends SystemService {
throw new RemoteException("frontendHandle can't be null");
}
synchronized (mLock) {
- try {
- return requestFrontendInternal(request, frontendHandle);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (!checkClientExists(request.getClientId())) {
+ throw new RemoteException("Request frontend from unregistered client:"
+ + request.getClientId());
}
+ return requestFrontendInternal(request, frontendHandle);
}
}
@@ -211,32 +211,45 @@ public class TunerResourceManagerService extends SystemService {
throw new RemoteException("demuxHandle can't be null");
}
synchronized (mLock) {
+ if (!checkClientExists(request.getClientId())) {
+ throw new RemoteException("Request demux from unregistered client:"
+ + request.getClientId());
+ }
return requestDemuxInternal(request, demuxHandle);
}
}
@Override
public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
- @NonNull int[] descrambleHandle) throws RemoteException {
+ @NonNull int[] descramblerHandle) throws RemoteException {
enforceDescramblerAccessPermission("requestDescrambler");
enforceTrmAccessPermission("requestDescrambler");
- if (descrambleHandle == null) {
- throw new RemoteException("descrambleHandle can't be null");
+ if (descramblerHandle == null) {
+ throw new RemoteException("descramblerHandle can't be null");
}
synchronized (mLock) {
- return requestDescramblerInternal(request, descrambleHandle);
+ if (!checkClientExists(request.getClientId())) {
+ throw new RemoteException("Request descrambler from unregistered client:"
+ + request.getClientId());
+ }
+ return requestDescramblerInternal(request, descramblerHandle);
}
}
@Override
- public boolean requestCasSession(
- @NonNull CasSessionRequest request, @NonNull int[] sessionResourceHandle) {
+ public boolean requestCasSession(@NonNull CasSessionRequest request,
+ @NonNull int[] casSessionHandle) throws RemoteException {
enforceTrmAccessPermission("requestCasSession");
- if (DEBUG) {
- Slog.d(TAG, "requestCasSession(request=" + request + ")");
+ if (casSessionHandle == null) {
+ throw new RemoteException("casSessionHandle can't be null");
+ }
+ synchronized (mLock) {
+ if (!checkClientExists(request.getClientId())) {
+ throw new RemoteException("Request cas from unregistered client:"
+ + request.getClientId());
+ }
+ return requestCasSessionInternal(request, casSessionHandle);
}
-
- return true;
}
@Override
@@ -248,11 +261,11 @@ public class TunerResourceManagerService extends SystemService {
throw new RemoteException("lnbHandle can't be null");
}
synchronized (mLock) {
- try {
- return requestLnbInternal(request, lnbHandle);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (!checkClientExists(request.getClientId())) {
+ throw new RemoteException("Request lnb from unregistered client:"
+ + request.getClientId());
}
+ return requestLnbInternal(request, lnbHandle);
}
}
@@ -264,15 +277,20 @@ public class TunerResourceManagerService extends SystemService {
frontendHandle)) {
throw new RemoteException("frontendHandle can't be invalid");
}
- int frontendId = getResourceIdFromHandle(frontendHandle);
- FrontendResource fe = getFrontendResource(frontendId);
- if (fe == null) {
- throw new RemoteException("Releasing frontend does not exist.");
- }
- if (fe.getOwnerClientId() != clientId) {
- throw new RemoteException("Client is not the current owner of the releasing fe.");
- }
synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release frontend from unregistered client:"
+ + clientId);
+ }
+ int frontendId = getResourceIdFromHandle(frontendHandle);
+ FrontendResource fe = getFrontendResource(frontendId);
+ if (fe == null) {
+ throw new RemoteException("Releasing frontend does not exist.");
+ }
+ if (fe.getOwnerClientId() != clientId) {
+ throw new RemoteException(
+ "Client is not the current owner of the releasing fe.");
+ }
releaseFrontendInternal(fe);
}
}
@@ -296,10 +314,26 @@ public class TunerResourceManagerService extends SystemService {
}
@Override
- public void releaseCasSession(int sessionResourceId, int clientId) {
+ public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException {
enforceTrmAccessPermission("releaseCasSession");
- if (DEBUG) {
- Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")");
+ if (!validateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) {
+ throw new RemoteException("casSessionHandle can't be invalid");
+ }
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release cas from unregistered client:" + clientId);
+ }
+ int casSystemId = getClientProfile(clientId).getInUseCasSystemId();
+ CasResource cas = getCasResource(casSystemId);
+ if (cas == null) {
+ throw new RemoteException("Releasing cas does not exist.");
+ }
+ if (!cas.getOwnerClientIds().contains(clientId)) {
+ throw new RemoteException(
+ "Client is not the current owner of the releasing cas.");
+ }
+ releaseCasSessionInternal(cas, clientId);
}
}
@@ -310,6 +344,9 @@ public class TunerResourceManagerService extends SystemService {
if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) {
throw new RemoteException("lnbHandle can't be invalid");
}
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release lnb from unregistered client:" + clientId);
+ }
int lnbId = getResourceIdFromHandle(lnbHandle);
LnbResource lnb = getLnbResource(lnbId);
if (lnb == null) {
@@ -465,17 +502,42 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
- protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle)
- throws RemoteException {
+ protected void updateCasInfoInternal(int casSystemId, int maxSessionNum) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "updateCasInfo(casSystemId=" + casSystemId
+ + ", maxSessionNum=" + maxSessionNum + ")");
+ }
+ // If maxSessionNum is 0, removing the Cas Resource.
+ if (maxSessionNum == 0) {
+ removeCasResource(casSystemId);
+ return;
+ }
+ // If the Cas exists, updates the Cas Resource accordingly.
+ CasResource cas = getCasResource(casSystemId);
+ if (cas != null) {
+ if (cas.getUsedSessionNum() > maxSessionNum) {
+ // Sort and release the short number of Cas resources.
+ int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum;
+ releaseLowerPriorityClientCasResources(releasingCasResourceNum);
+ }
+ cas.updateMaxSessionNum(maxSessionNum);
+ return;
+ }
+ // Add the new Cas Resource.
+ cas = new CasResource.Builder(casSystemId)
+ .maxSessionNum(maxSessionNum)
+ .build();
+ addCasResource(cas);
+ }
+
+ @VisibleForTesting
+ protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) {
if (DEBUG) {
Slog.d(TAG, "requestFrontend(request=" + request + ")");
}
frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- if (!checkClientExists(request.getClientId())) {
- Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId());
- return false;
- }
ClientProfile requestClient = getClientProfile(request.getClientId());
int grantingFrontendId = -1;
int inUseLowestPriorityFrId = -1;
@@ -496,7 +558,7 @@ public class TunerResourceManagerService extends SystemService {
} else if (grantingFrontendId < 0) {
// Record the frontend id with the lowest client priority among all the
// in use frontends when no available frontend has been found.
- int priority = getOwnerClientPriority(fr);
+ int priority = getOwnerClientPriority(fr.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityFrId = fr.getId();
currentLowestPriority = priority;
@@ -530,17 +592,12 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
- protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)
- throws RemoteException {
+ protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
if (DEBUG) {
Slog.d(TAG, "requestLnb(request=" + request + ")");
}
lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- if (!checkClientExists(request.getClientId())) {
- Slog.e(TAG, "Request lnb from unregistered client:" + request.getClientId());
- return false;
- }
ClientProfile requestClient = getClientProfile(request.getClientId());
int grantingLnbId = -1;
int inUseLowestPriorityLnbId = -1;
@@ -554,7 +611,7 @@ public class TunerResourceManagerService extends SystemService {
} else {
// Record the lnb id with the lowest client priority among all the
// in use lnb when no available lnb has been found.
- int priority = getOwnerClientPriority(lnb);
+ int priority = getOwnerClientPriority(lnb.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityLnbId = lnb.getId();
currentLowestPriority = priority;
@@ -588,7 +645,55 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
- void releaseFrontendInternal(FrontendResource fe) {
+ protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) {
+ if (DEBUG) {
+ Slog.d(TAG, "requestCasSession(request=" + request + ")");
+ }
+ CasResource cas = getCasResource(request.getCasSystemId());
+ // Unregistered Cas System is treated as having unlimited sessions.
+ if (cas == null) {
+ cas = new CasResource.Builder(request.getCasSystemId())
+ .maxSessionNum(Integer.MAX_VALUE)
+ .build();
+ addCasResource(cas);
+ }
+ casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ ClientProfile requestClient = getClientProfile(request.getClientId());
+ int lowestPriorityOwnerId = -1;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ if (!cas.isFullyUsed()) {
+ casSessionHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
+ updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId());
+ return true;
+ }
+ for (int ownerId : cas.getOwnerClientIds()) {
+ // Record the client id with lowest priority that is using the current Cas system.
+ int priority = getOwnerClientPriority(ownerId);
+ if (currentLowestPriority > priority) {
+ lowestPriorityOwnerId = ownerId;
+ currentLowestPriority = priority;
+ }
+ }
+
+ // When all the Cas sessions are occupied, reclaim the lowest priority client if the
+ // request client has higher priority.
+ if (lowestPriorityOwnerId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
+ if (!reclaimResource(lowestPriorityOwnerId,
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) {
+ return false;
+ }
+ casSessionHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
+ updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId());
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ protected void releaseFrontendInternal(FrontendResource fe) {
if (DEBUG) {
Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ")");
}
@@ -596,7 +701,7 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
- void releaseLnbInternal(LnbResource lnb) {
+ protected void releaseLnbInternal(LnbResource lnb) {
if (DEBUG) {
Slog.d(TAG, "releaseLnb(lnbId=" + lnb.getId() + ")");
}
@@ -604,7 +709,15 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
- boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+ protected void releaseCasSessionInternal(CasResource cas, int ownerClientId) {
+ if (DEBUG) {
+ Slog.d(TAG, "releaseCasSession(sessionResourceId=" + cas.getSystemId() + ")");
+ }
+ updateCasClientMappingOnRelease(cas, ownerClientId);
+ }
+
+ @VisibleForTesting
+ protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
if (DEBUG) {
Slog.d(TAG, "requestDemux(request=" + request + ")");
}
@@ -614,7 +727,8 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
- boolean requestDescramblerInternal(TunerDescramblerRequest request, int[] descramblerHandle) {
+ protected boolean requestDescramblerInternal(
+ TunerDescramblerRequest request, int[] descramblerHandle) {
if (DEBUG) {
Slog.d(TAG, "requestDescrambler(request=" + request + ")");
}
@@ -742,14 +856,28 @@ public class TunerResourceManagerService extends SystemService {
ownerProfile.releaseLnb(releasingLnb.getId());
}
+ private void updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId) {
+ CasResource grantingCas = getCasResource(grantingId);
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ grantingCas.setOwner(ownerClientId);
+ ownerProfile.useCas(grantingId);
+ }
+
+ private void updateCasClientMappingOnRelease(
+ @NonNull CasResource releasingCas, int ownerClientId) {
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ releasingCas.removeOwner(ownerClientId);
+ ownerProfile.releaseCas();
+ }
+
/**
* Get the owner client's priority from the resource id.
*
- * @param resource a in use tuner resource.
+ * @param clientId the owner client id.
* @return the priority of the owner client of the resource.
*/
- private int getOwnerClientPriority(TunerResourceBasic resource) {
- return getClientProfile(resource.getOwnerClientId()).getPriority();
+ private int getOwnerClientPriority(int clientId) {
+ return getClientProfile(clientId).getPriority();
}
@VisibleForTesting
@@ -783,6 +911,9 @@ public class TunerResourceManagerService extends SystemService {
private void removeFrontendResource(int removingId) {
FrontendResource fe = getFrontendResource(removingId);
+ if (fe == null) {
+ return;
+ }
if (fe.isInUse()) {
releaseFrontendInternal(fe);
}
@@ -811,6 +942,9 @@ public class TunerResourceManagerService extends SystemService {
private void removeLnbResource(int removingId) {
LnbResource lnb = getLnbResource(removingId);
+ if (lnb == null) {
+ return;
+ }
if (lnb.isInUse()) {
releaseLnbInternal(lnb);
}
@@ -819,6 +953,39 @@ public class TunerResourceManagerService extends SystemService {
@VisibleForTesting
@Nullable
+ protected CasResource getCasResource(int systemId) {
+ return mCasResources.get(systemId);
+ }
+
+ @VisibleForTesting
+ protected Map<Integer, CasResource> getCasResources() {
+ return mCasResources;
+ }
+
+ private void addCasResource(CasResource newCas) {
+ // Update resource list and available id list
+ mCasResources.put(newCas.getSystemId(), newCas);
+ }
+
+ private void removeCasResource(int removingId) {
+ CasResource cas = getCasResource(removingId);
+ if (cas == null) {
+ return;
+ }
+ for (int ownerId : cas.getOwnerClientIds()) {
+ getClientProfile(ownerId).releaseCas();
+ }
+ mCasResources.remove(removingId);
+ }
+
+ private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) {
+ // TODO: Sort with a treemap
+
+ // select the first num client to release
+ }
+
+ @VisibleForTesting
+ @Nullable
protected ClientProfile getClientProfile(int clientId) {
return mClientProfiles.get(clientId);
}
@@ -830,12 +997,7 @@ public class TunerResourceManagerService extends SystemService {
}
private void removeClientProfile(int clientId) {
- for (int id : getClientProfile(clientId).getInUseFrontendIds()) {
- getFrontendResource(id).removeOwner();
- for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) {
- getFrontendResource(groupMemberId).removeOwner();
- }
- }
+ reclaimingResourcesFromClient(getClientProfile(clientId));
mClientProfiles.remove(clientId);
mListeners.remove(clientId);
}
@@ -847,6 +1009,9 @@ public class TunerResourceManagerService extends SystemService {
for (Integer lnbId : profile.getInUseLnbIds()) {
getLnbResource(lnbId).removeOwner();
}
+ if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) {
+ getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId());
+ }
profile.reclaimAllResources();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 736bc9f8bc22..93a757449564 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5400,14 +5400,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
&& config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
&& config.navigation == Configuration.NAVIGATION_NONAV);
- int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
- final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
- && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
- && modeType != Configuration.UI_MODE_TYPE_TELEVISION
- && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
HIDE_ERROR_DIALOGS, 0) != 0;
- mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
+ mShowDialogs = inputMethodExists
+ && ActivityTaskManager.currentUiModeSupportsErrorDialogs(mContext)
+ && !hideDialogsSet;
}
private void updateFontScaleIfNeeded(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e9d3d56ee283..8aace212d094 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -261,6 +261,8 @@ public class DisplayPolicy {
@Px
private int mRightGestureInset;
+ private boolean mNavButtonForcedVisible;
+
StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAcquireLock) {
if (mStatusBarManagerInternal == null) {
@@ -1046,12 +1048,14 @@ public class DisplayPolicy {
// calculate inset.
if (navigationBarPosition(displayFrames.mDisplayWidth,
displayFrames.mDisplayHeight,
- displayFrames.mRotation) == NAV_BAR_BOTTOM) {
+ displayFrames.mRotation) == NAV_BAR_BOTTOM
+ && !mNavButtonForcedVisible) {
+
sTmpRect.set(displayFrames.mUnrestricted);
sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
inOutFrame.top = sTmpRect.bottom
- getNavigationBarHeight(displayFrames.mRotation,
- mDisplayContent.getConfiguration().uiMode);
+ mDisplayContent.getConfiguration().uiMode);
}
},
@@ -2810,6 +2814,8 @@ public class DisplayPolicy {
mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode);
mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res);
+ mNavButtonForcedVisible =
+ mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough);
mNavigationBarAlwaysShowOnSideGesture =
res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture);
@@ -3783,13 +3789,14 @@ public class DisplayPolicy {
* @param screenshotType The type of screenshot, for example either
* {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or
* {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION}
+ * @param source Where the screenshot originated from (see WindowManager.ScreenshotSource)
*/
- public void takeScreenshot(int screenshotType) {
+ public void takeScreenshot(int screenshotType, int source) {
if (mScreenshotHelper != null) {
mScreenshotHelper.takeScreenshot(screenshotType,
mStatusBar != null && mStatusBar.isVisibleLw(),
mNavigationBar != null && mNavigationBar.isVisibleLw(),
- mHandler, null /* completionConsumer */);
+ source, mHandler, null /* completionConsumer */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a5014145aa60..51095ee85eea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2446,17 +2446,15 @@ public class WindowManagerService extends IWindowManager.Stub
if (controls != null) {
final int length = Math.min(controls.length, outControls.length);
for (int i = 0; i < length; i++) {
- final InsetsSourceControl control = controls[i];
-
- // Check if we are sending invalid leashes.
- final SurfaceControl leash = control != null ? control.getLeash() : null;
- if (leash != null && !leash.isValid()) {
- Slog.wtf(TAG, leash + " is not valid before sending to " + win,
- leash.getReleaseStack());
- }
-
- outControls[i] = win.isClientLocal() && control != null
- ? new InsetsSourceControl(control) : control;
+ // We will leave the critical section before returning the leash to the client,
+ // so we need to copy the leash to prevent others release the one that we are
+ // about to return.
+ // TODO: We will have an extra copy if the client is not local.
+ // For now, we rely on GC to release it.
+ // Maybe we can modify InsetsSourceControl.writeToParcel so it can release
+ // the extra leash as soon as possible.
+ outControls[i] = controls[i] != null
+ ? new InsetsSourceControl(controls[i]) : null;
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 22b0b62a819d..7b845557cf73 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -161,8 +161,11 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentSender;
import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -171,6 +174,7 @@ import android.content.pm.CrossProfileAppsInternal;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
@@ -2704,6 +2708,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
clearDeviceOwnerLocked(doAdmin, doUserId);
Slog.i(LOG_TAG, "Removing admin artifacts...");
removeAdminArtifacts(doAdminReceiver, doUserId);
+ Slog.i(LOG_TAG, "Uninstalling the DO...");
+ uninstallOrDisablePackage(doAdminComponent.getPackageName(), doUserId);
Slog.i(LOG_TAG, "Migration complete.");
// Note: KeyChain keys are not removed and will remain accessible for the apps that have
@@ -2715,6 +2721,47 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
}
+ private void uninstallOrDisablePackage(String packageName, int userHandle) {
+ final ApplicationInfo appInfo;
+ try {
+ appInfo = mIPackageManager.getApplicationInfo(
+ packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userHandle);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ return;
+ }
+ if (appInfo == null) {
+ Slog.wtf(LOG_TAG, "Failed to get package info for " + packageName);
+ return;
+ }
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ Slog.i(LOG_TAG, String.format(
+ "Package %s is pre-installed, marking disabled until used", packageName));
+ mContext.getPackageManager().setApplicationEnabledSetting(packageName,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0 /* flags */);
+ return;
+ }
+
+ final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ final int status = intent.getIntExtra(
+ PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ Slog.i(LOG_TAG, String.format(
+ "Package %s uninstalled for user %d", packageName, userHandle));
+ } else {
+ Slog.e(LOG_TAG, String.format(
+ "Failed to uninstall %s; status: %d", packageName, status));
+ }
+ }
+ };
+
+ final PackageInstaller pi = mInjector.getPackageManager(userHandle).getPackageInstaller();
+ pi.uninstall(packageName, 0 /* flags */, new IntentSender((IIntentSender) mLocalSender));
+ }
+
private void moveDoPoliciesToProfileParentAdmin(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
// The following policies can be already controlled via parent instance, skip if so.
if (parentAdmin.mPasswordPolicy.quality == PASSWORD_QUALITY_UNSPECIFIED) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 335217719cc9..064e3486823a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS;
+
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
@@ -308,7 +310,7 @@ public class SystemActionPerformerTest {
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
verify(mMockScreenshotHelper).takeScreenshot(
eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
- anyBoolean(), any(Handler.class), any());
+ anyBoolean(), eq(SCREENSHOT_ACCESSIBILITY_ACTIONS), any(Handler.class), any());
}
// PendingIntent is a final class and cannot be mocked. So we are using this
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 965304f3c433..21af3563b869 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -26,6 +26,7 @@ import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputService;
import android.media.tv.tuner.frontend.FrontendSettings;
+import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
@@ -34,7 +35,6 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
-import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -236,12 +236,8 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isFalse();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@@ -264,12 +260,8 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isFalse();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@@ -296,12 +288,8 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(0);
}
@@ -334,23 +322,15 @@ public class TunerResourceManagerServiceTest {
int[] frontendHandle = new int[1];
TunerFrontendRequest request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(infos[0].getId());
request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -394,32 +374,20 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isRelaimed()).isFalse();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
+ assertThat(listener.isRelaimed()).isFalse();
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isRelaimed()).isFalse();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
+ assertThat(listener.isRelaimed()).isFalse();
}
@Test
@@ -456,12 +424,8 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(infos[0].getId());
assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
@@ -470,12 +434,8 @@ public class TunerResourceManagerServiceTest {
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
@@ -511,12 +471,8 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
int frontendId = mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]);
assertThat(frontendId).isEqualTo(infos[0].getId());
assertThat(mTunerResourceManagerService
@@ -534,6 +490,99 @@ public class TunerResourceManagerServiceTest {
}
@Test
+ public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientPriorities = {100, 500};
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[0], listener, clientId0);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[1], new TestResourcesReclaimListener(), clientId1);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfile(clientId1[0])
+ .setPriority(clientPriorities[1]);
+
+ // Init cas resources.
+ mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
+
+ CasSessionRequest request = new CasSessionRequest(clientId0[0], 1 /*casSystemId*/);
+ int[] casSessionHandle = new int[1];
+ // Request for 2 cas sessions.
+ assertThat(mTunerResourceManagerService
+ .requestCasSessionInternal(request, casSessionHandle)).isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestCasSessionInternal(request, casSessionHandle)).isTrue();
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
+ .isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .getInUseCasSystemId()).isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getCasResource(1)
+ .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+ assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
+
+ request = new CasSessionRequest(clientId1[0], 1);
+ assertThat(mTunerResourceManagerService
+ .requestCasSessionInternal(request, casSessionHandle)).isTrue();
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
+ .isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
+ .getInUseCasSystemId()).isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(mTunerResourceManagerService.getCasResource(1)
+ .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
+ assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
+ assertThat(listener.isRelaimed()).isTrue();
+ }
+
+ @Test
+ public void releaseCasTest() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[1];
+ profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
+ mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init cas resources.
+ mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
+
+ CasSessionRequest request = new CasSessionRequest(clientId[0], 1 /*casSystemId*/);
+ int[] casSessionHandle = new int[1];
+ // Request for 1 cas sessions.
+ assertThat(mTunerResourceManagerService
+ .requestCasSessionInternal(request, casSessionHandle)).isTrue();
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
+ .isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
+ .getInUseCasSystemId()).isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getCasResource(1)
+ .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
+ assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
+
+ // Release cas
+ mTunerResourceManagerService.releaseCasSessionInternal(mTunerResourceManagerService
+ .getCasResource(1), clientId[0]);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
+ .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
+ assertThat(mTunerResourceManagerService.getCasResource(1)
+ .getOwnerClientIds()).isEmpty();
+ }
+
+ @Test
public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[2];
@@ -562,24 +611,16 @@ public class TunerResourceManagerServiceTest {
TunerLnbRequest request = new TunerLnbRequest(clientId0[0]);
int[] lnbHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestLnbInternal(request, lnbHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]))
.isEqualTo(lnbIds[0]);
assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
.getInUseLnbIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(lnbIds[0])));
request = new TunerLnbRequest(clientId1[0]);
- try {
- assertThat(mTunerResourceManagerService
- .requestLnbInternal(request, lnbHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]))
.isEqualTo(lnbIds[0]);
assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0])
@@ -608,12 +649,8 @@ public class TunerResourceManagerServiceTest {
TunerLnbRequest request = new TunerLnbRequest(clientId[0]);
int[] lnbHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestLnbInternal(request, lnbHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestLnbInternal(request, lnbHandle)).isTrue();
int lnbId = mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]);
assertThat(lnbId).isEqualTo(lnbIds[0]);
@@ -647,12 +684,8 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
- try {
- assertThat(mTunerResourceManagerService
- .requestFrontendInternal(request, frontendHandle)).isTrue();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
.isEqualTo(infos[0].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9dfa3ac7a5d8..fa9909547fc4 100755
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -734,6 +734,31 @@ public abstract class Connection extends Conferenceable {
"android.telecom.extra.ORIGINAL_CONNECTION_ID";
/**
+ * Extra key set on a {@link Connection} when it was created via a remote connection service.
+ * For example, if a connection manager requests a remote connection service to create a call
+ * using one of the remote connection service's phone account handle, this extra will be set so
+ * that Telecom knows that the wrapped remote connection originated in a remote connection
+ * service. We stash this in the extras since connection managers will typically copy the
+ * extras from a {@link RemoteConnection} to a {@link Connection} (there is ultimately not
+ * other way to relate a {@link RemoteConnection} to a {@link Connection}.
+ * @hide
+ */
+ public static final String EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE =
+ "android.telecom.extra.REMOTE_PHONE_ACCOUNT_HANDLE";
+
+ /**
+ * Extra key set from a {@link ConnectionService} when using the remote connection APIs
+ * (e.g. {@link RemoteConnectionService#createRemoteConnection(PhoneAccountHandle,
+ * ConnectionRequest, boolean)}) to create a remote connection. Provides the receiving
+ * {@link ConnectionService} with a means to know the package name of the requesting
+ * {@link ConnectionService} so that {@link #EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE} can be set for
+ * better visibility in Telecom of where a connection ultimately originated.
+ * @hide
+ */
+ public static final String EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME =
+ "android.telecom.extra.REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME";
+
+ /**
* Boolean connection extra key set on the extras passed to
* {@link Connection#sendConnectionEvent} which indicates that audio is present
* on the RTT call when the extra value is true.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 1b60e4820ad0..a716b37f7efd 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1859,9 +1859,25 @@ public abstract class ConnectionService extends Service {
new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"),
request.getAccountHandle());
}
- if (conference.getExtras() != null) {
- conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+
+ Bundle extras = request.getExtras();
+ Bundle newExtras = new Bundle();
+ newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+ if (extras != null) {
+ // If the request originated from a remote connection service, we will add some
+ // tracking information that Telecom can use to keep informed of which package
+ // made the remote request, and which remote connection service was used.
+ if (extras.containsKey(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
+ newExtras.putString(
+ Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
+ extras.getString(
+ Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME));
+ newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
+ request.getAccountHandle());
+ }
}
+ conference.putExtras(newExtras);
+
mConferenceById.put(callId, conference);
mIdByConference.put(conference, callId);
conference.addListener(mConferenceListener);
@@ -1936,6 +1952,30 @@ public abstract class ConnectionService extends Service {
Log.i(this, "createConnection, implementation returned null connection.");
connection = Connection.createFailedConnection(
new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"));
+ } else {
+ try {
+ Bundle extras = request.getExtras();
+ if (extras != null) {
+ // If the request originated from a remote connection service, we will add some
+ // tracking information that Telecom can use to keep informed of which package
+ // made the remote request, and which remote connection service was used.
+ if (extras.containsKey(
+ Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) {
+ Bundle newExtras = new Bundle();
+ newExtras.putString(
+ Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
+ extras.getString(
+ Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME
+ ));
+ newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
+ request.getAccountHandle());
+ connection.putExtras(newExtras);
+ }
+ }
+ } catch (UnsupportedOperationException ose) {
+ // Do nothing; if the ConnectionService reported a failure it will be an instance
+ // of an immutable Connection which we cannot edit, so we're out of luck.
+ }
}
boolean isSelfManaged =
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index d82e93fac76d..8d3f4e1df8bc 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -427,7 +427,7 @@ public class Session {
StringBuilder methodName = new StringBuilder();
methodName.append(getFullMethodPath(false /*truncatePath*/));
if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) {
- methodName.append("(InCall package: ");
+ methodName.append("(");
methodName.append(mOwnerInfo);
methodName.append(")");
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index cad5b707a146..a0833011715d 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -258,6 +258,9 @@ final class RemoteConnectionService {
// See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information.
Bundle newExtras = new Bundle();
newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+ // Track the fact this request was relayed through the remote connection service.
+ newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
+ parcel.getPhoneAccount());
conference.putExtras(newExtras);
conference.registerCallback(new RemoteConference.Callback() {
@@ -383,6 +386,11 @@ final class RemoteConnectionService {
RemoteConnection remoteConnection = new RemoteConnection(callId,
mOutgoingConnectionServiceRpc, connection, callingPackage,
callingTargetSdkVersion);
+ // Track that it is via a remote connection.
+ Bundle newExtras = new Bundle();
+ newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
+ connection.getPhoneAccount());
+ remoteConnection.putExtras(newExtras);
mConnectionById.put(callId, remoteConnection);
remoteConnection.registerCallback(new RemoteConnection.Callback() {
@Override
@@ -535,10 +543,20 @@ final class RemoteConnectionService {
ConnectionRequest request,
boolean isIncoming) {
final String id = UUID.randomUUID().toString();
+ Bundle extras = new Bundle();
+ if (request.getExtras() != null) {
+ extras.putAll(request.getExtras());
+ }
+ // We will set the package name for the originator of the remote request; this lets the
+ // receiving ConnectionService know that the request originated from a remote connection
+ // service so that it can provide tracking information for Telecom.
+ extras.putString(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME,
+ mOurConnectionServiceImpl.getApplicationContext().getOpPackageName());
+
final ConnectionRequest newRequest = new ConnectionRequest.Builder()
.setAccountHandle(request.getAccountHandle())
.setAddress(request.getAddress())
- .setExtras(request.getExtras())
+ .setExtras(extras)
.setVideoState(request.getVideoState())
.setRttPipeFromInCall(request.getRttPipeFromInCall())
.setRttPipeToInCall(request.getRttPipeToInCall())